将焦点设置到.NET UserControl...?

12

我正在创建一个派生自UserControl的自定义控件,我想将焦点设置到它上面。

这个自定义控件包含一个ComboBox控件和我在旁边绘制一些字符串。

ComboBox可以接收焦点,但我想能够将焦点设置到UserControl本身。 当我创建自定义控件并从父窗体调用CanFocus()时,我的OnPaint处理程序被设置为在具有焦点时略微不同,但它返回false。

是否有属性或其他内容需要设置?

7个回答

22

UserControl将会竭尽全力避免获取焦点。它有自动将焦点传递给子控件(如果存在)的代码,如果它确实获得了焦点。您至少需要覆盖WndProc()并拦截WM_SETFOCUS消息。可能还需要进行其他手术,如ControlStyles.Selectable和TabStop以及TabIndex属性。

您接下来面临的问题是,当UserControl确实具有焦点时,它不会对键盘消息做出有意义的响应。您需要检测UC背景上的点击以处理鼠标消息,并覆盖绘图,以使用户明确UC具有焦点(使用ControlPaint.DrawFocusRectangle)。如果这开始听起来不太吸引人,那是因为UC实际上是一个容器控件。


1
这似乎完成了任务。我会继续尝试并解决你提到的复杂问题。谢谢! - Sambo
1
很高兴从 NoBugz 获得了另一个很棒的答案。好奇地问:在这种情况下,您是否认为 OP 使用表单而不是用户控件会更好? - BillW
2
@Bill:Form类也是一个容器控件,虽然它不会像其他控件那样反抗。我的建议是避免使用非标准的UI实践,这也会让用户感到困惑。 - Hans Passant
1
如果UserControl旨在成为容器控件,那么我们可以从中派生出旨在成为自定义控件的内容吗?我们应该只是从Control或ScrollableControl派生吗? - Trevor Elliott
我知道,这是几年前的事了...但其他人可能会遇到这个问题(就像我一样)。这里的链接应该可以回答这个问题,并且非常简单。 - infero

1

太长了,不适合作为评论,包含链接和代码……但这只是一条评论而已……

很多人抱怨一个UserControl没有触发“GotFocus()”事件。例如: UserControl and GotFocus() FYI:根据我的经验,“LostFocus()”会按预期触发。在过去的多个窗体项目中,我尝试在每个窗体上实现“Enter”和“Leave”事件处理程序,并发现“Enter”仅在窗体加载时调用一次。

显然,UserControl上的控件“获取焦点”(以一种我无法解释的方式,但也许SO的WinForms专家之一会)。也许这与UserControl从ContainerControl继承有关?

我尝试编写一个“GotFocus()”处理程序:

    private void Control_GotFocus(object sender, EventArgs e)
    {
        Console.WriteLine("Control GotFocus : " + ((sender as Control).Name));
    }

然后,在UserControl的“Load”事件中,将UserControl上的所有控件与该事件处理程序关联,观察到具有最低TabIndex的UserControl上的控件在启动应用程序时以及在窗体之间切换时会触发“GotFocus”事件。
在这种情况下,我看到的唯一其他提到的事情是确保UserControl的“IsTabStop”属性设置为“True”:这是来自MS的Shawn Wildermuth针对SilverLight相关问题的建议,所以不知道是否适用于您的情况。
另一个建议是为UserControl编写MouseDown或MouseClick事件处理程序,并在其中调用:this.SetFocus();,但这个方法并没有成功。
希望你能得到答案!

1

在某些情况下,不希望焦点移动到UserControl的子元素。
在这种情况下,还需要将ControlStyles.ContainerControl设置为false。

Public Sub New()
    InitializeComponent()

    Me.SetStyle(ControlStyles.ContainerControl, False)
    Me.SetStyle(ControlStyles.Selectable, True)

End Sub

1

1
感谢您的回复...我确认我的控件有一个句柄值。该控件可见且启用,我显示它的面板也可见且启用,并且它们都出现在窗体上...!这似乎符合先决条件...我重写了OnGotFocus()并设置了断点,但它从未被触发。此外,我在我的窗体对象中为控件的GotFocus事件设置了处理程序,但代码再次没有被触发。还有其他想法吗...? - Sambo
这些先决条件适用于 Control,但 UserControl 会破坏它,正如 @HansPassant 上面提到的那样。 - Scott Baker

0
如果UserControl获得焦点,它会将焦点内部传递给其子控件。
因此,您需要跳过设置焦点到子控件的代码的执行。为此,您需要重写WndProc()并跳过任何WM_SETFOCUS消息的执行。
 public class FocusableUserControl : UserControl
 {
    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case (int)Win32Constants.WM_SETFOCUS:
            //Returning from here will skip setting focus to child controls.
            //It will not skip setting focus to this control.

                Console.WriteLine("FocusableUserControl is focused: " + Focused);
                return;
        }

        base.WndProc(ref m);
    }
}

WM_SETFOCUS 的值为 "0x0007"。


0
假设您在用户控件上有一张图片,并且想要通过模拟“GetFocus”事件来突出显示它(例如,当用户控件获得焦点时,这张图片会被突出显示)。用户控件的焦点将通过在PictureBox上绘制虚线轮廓来处理。这是通过用户控件的OnEnter和OnLeave事件完成的。以下是突出显示过程...
Public Sub highlightImage()
    Dim l As Single() = {2, 2, 2, 2}
    Dim p As New Pen(Color.Gray, 1)
    p.DashPattern = l
    Dim g As Graphics = picColor.CreateGraphics()
    g.DrawRectangle(p, 0, 0, picColor.Width - 1, picColor.Height - 1)
End Sub

这两个重写将完成工作。

Protected Overrides Sub OnEnter(e As EventArgs)
    MyBase.OnEnter(e)
    Me.highlightImage()
End Sub
Protected Overrides Sub OnLeave(e As EventArgs)
    MyBase.OnLeave(e)
    MyBase.Refresh()
End Sub

0
汉斯是对的,一个用户控件将尽其所能将焦点移交给子控件,但在这种狡猾的情况下除外:
  • 所有子项都已启用== false
这意味着通过一些准备工作,用户控件会不情愿地接受焦点。 尝试类似以下的内容:
var controls = this.Controls.Cast<Control>().ToList();
controls.ForEach(control => control.Enabled = false);
this.ActiveControl = null; //the UserControl will try to remember its ActiveControl
this.Focus();
controls.ForEach(control => control.Enabled = true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接