我真的需要在自定义控件的OnMouseDown事件中调用Focus吗?

9

我正在实现一个继承自Control的自定义控件。我希望它可以被聚焦 (它是一种列表框)。

在构造函数中,我执行以下操作:

SetStyle(ControlStyles.Selectable, true);

现在我可以使用Tab键导航到控件。

然而,当控件接收到鼠标点击时,它并不会自动获得焦点。当然,我可以解决这个问题:

protected override void OnMouseDown(MouseEventArgs e)
{
    Focus();
    base.OnMouseDown(e);
}

但这种方法感觉像是一个不必要的补救措施。这真的是正确的方法吗?还是有一种方法可以告诉Control在接收到鼠标点击时自动获取焦点?


@Thomas:你提到“我已经有了焦点矩形”,是通过重写OnGotFocus和OnLostFocus并调用ControlPaint.DrawFocusRectangle来实现的吗?感谢任何回复。 - BillW
1
@BillW:没错,这就是我的做法。OnGotFocusOnLostFocus 会使包含焦点矩形的矩形无效;OnPaint 检查 Focused 属性并根据需要调用 ControlPaint.DrawFocusRectangle - Thomas
感谢您对我的问题的耐心回答!我发表了另一个答案,这是我能让它在针对Framework 3.5(完整版)编译的Visual Studio RC1中工作的唯一方法。所谓的“工作”就是指当控件“被选定”时,它会显示选区矩形,并且当“移开焦点”时,选区矩形会消失:当单击控件时,选区矩形会再次显示。 - BillW
3个回答

4
拆卸来拯救!事实证明,

SetStyle(ControlStyles.UserMouse, true);

做到了。
具有讽刺意味的是,我在文档中读到了以下内容:“UserMouse”:如果为true,则控件会处理自己的鼠标事件,而操作系统不会处理鼠标事件。这似乎与我想要的完全相反,所以我只尝试将其设置为false... WinForms文档真是太棒了。

2
这不正确。只有在包装本地控件并实现自己的鼠标消息处理的控件类周围时,才应该关闭UserMouse样式。您允许控件获得焦点是因为您对输入事件感兴趣。故意关闭输入事件处理是没有意义的。 - Hans Passant
“控件自己处理鼠标”这句话在你的澄清下更加令人困惑:是WinForms控件自己处理鼠标(覆盖OnMouseDown等),还是本地控件自己处理鼠标(这意味着WinForms控件应该远离它)?无论如何,显然默认情况下UserMousefalse。而且由于我没有包装本地控件,所以应该是true,对吗? - Thomas
是的,它会调用OnMouseDown方法。您可以重写该方法以赋予您自己的控件其鼠标行为,例如使其获得焦点。您的控件继承自Control,没有包装任何本地Windows控件(如ListBox或TreeView)。它只是一个普通的窗口。 - Hans Passant

2

是的,这就是你应该做的。有很多控件没有一个有意义的方式来获取焦点,例如PictureBox、Panel等从ContainerControl派生的任何控件。因此,在OnMouseDown()中,Control.OnMouseDown()不会自动调用Focus()。

仅仅覆盖OnMouseDown方法还不够,您还应该让用户清楚地知道您的控件拥有焦点。这样她就会知道键盘敲击的方向。这需要覆盖OnPaint(),以便您可以绘制一个焦点矩形。ControlPaint.DrawFocusRectangle()是一个针对此操作的样板实现。

但是,如果您不能对键盘消息进行有意义的处理,那么获取焦点就真的毫无用处了。因此,您必须覆盖OnKeyDown和/或OnKeyPressed,并向用户显示反馈,以便她能看到自己输入的内容。如果您没有一个有用的实现,那么就不应该获取焦点。这就是为什么PictureBox不会获取焦点的原因。


好的观点,但是你为什么认为我一开始想让控件可以聚焦呢?我已经放置了聚焦矩形,并且现在正在处理键盘处理程序。 - Thomas
3
在问题中提及这些细节总是很好的做法。 - Hans Passant
我在括号里提到它是一种列表框,并暗示它应该处理按键;我不想通过提及所有这些细节来分散注意力。但你说得对:提供关于“为什么你想要那个”的信息是很好的,因为有时候事实证明你实际上想要以完全不同的方式解决或规避潜在问题! - Thomas

0
  1. 在FrameWork 3.5的WinForms项目中编译

  2. 从工具箱中将Control1的实例拖到表单表面...确保其TabStop属性设置为“true”

  3. 在表单上放置其他控件。

  4. 验证当Control1的实例被选中时:它会显示一个选择矩形,当你“切换”离开它时,它会消失。

  5. 验证如果你点击Control1的实例,它会显示选择矩形,如果你点击其他控件,它会消失。

    namespace testFocusableControl
    {
        //  VS Studio 2010 RC1 : 对FrameWork 3.5 Full进行测试(不是'Client)
    
        public class Control1 : Control
        {
            public Control1()
            {
                SetStyle(ControlStyles.UserMouse, true);
            }
    
            protected override void OnLostFocus(EventArgs e)
            {
                this.Invalidate();
                base.OnLostFocus(e);
            }
    
            protected override void OnGotFocus(EventArgs e)
            {
                this.Invalidate();
                base.OnGotFocus(e);
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                if (this.Focused)
                {
                    ControlPaint.DrawFocusRectangle(e.Graphics, this.ClientRectangle, Color.Red, Color.Blue);
                }
                base.OnPaint(e);
            }
        }
    }
    

对我来说唯一的“未解决问题”是,这个解决方案将在鼠标单击时显示选择矩形,但我没有像Thomas建议的那样实现任何MouseDown代码。

请注意,如果您通过'SetStyle(ControlStyles.ContainerControl,true);使控件成为'ContainerControl,并向其添加其他控件,即使您将添加的控件的TabStop属性设置为'false:...如果单击它...它将获得焦点,并且您将失去在ContainerControl上显示的焦点矩形。


1
你标记为 // no effect 的方法不起作用,因为你将它们声明为 virtual 而不是 override。显然,UserPaintSelectable 默认为 true。至于第6点:无论代码是否有效,不理解代码都是不好的。但我认为我们现在已经明白了什么能够工作,什么不能工作以及原因,对吧? :) - Thomas
非常感谢,Thomas,谢谢你的见解。我已经修改了代码并融入了你的信息。 - BillW

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