简体   繁体   English

如何使控件失去焦点单击它之外的任何位置?

[英]How can I make a Control lose focus clicking anywhere outside of it?

在此处输入图像描述

I want my UserControl (at the middle-top in the image) to lose focus when I click outside of it, anywhere.我希望我的 UserControl(在图像的中间顶部)在我单击它之外的任何地方时失去焦点。

I have tried this:我试过这个:

private void Form1_Click(object sender, EventArgs e)
{
    ActiveControl = null;
}

It only works when I click the Form itself, the TrackBar and the MediaPlayer.它仅在我单击表单本身、TrackBar 和 MediaPlayer 时才有效。 It doesn't work when I click other controls, even the FlowLayoutPanel.当我单击其他控件时它不起作用,即使是 FlowLayoutPanel。
What can I do about it?我能做些什么呢?

Eric,埃里克,

To remove the focus from an item in your custom control, you will need some other focusable control to pass the focus to.要从自定义控件中的项目中移除焦点,您将需要一些其他可聚焦的控件来传递焦点。 I would do something like this:我会做这样的事情:

private void StealFocus() => lbl_SomeLabel.Focus();

And then drop StealFocus() in your form's click event handler:然后将StealFocus()放到表单的单击事件处理程序中:

private void Form1_Click(object sender, EventArgs e) => StealFocus();

and any other control the user might click that doesn't execute a command.以及用户可能单击但不执行命令的任何其他控件。

Note that you cannot set focus to the Form.请注意,您不能将焦点设置到窗体。 Container controls like Form and Panel will pass the Focus on to their first child control.像 Form 和 Panel 这样的容器控件会将焦点传递给它们的第一个子控件。 Which could be the custom control you wanted to remove focus from.这可能是您想要从中移除焦点的自定义控件。

You can use a general purpose Message handler/dispatcher:您可以使用通用消息处理程序/调度程序:

  • Make your Forms that need to provide control over Mouse events implement IMessageFilter , so these Forms will know beforehand what messages are sent to which child Control, what kind of event is generated and any other details that may be useful, such as the position of the Mouse pointer when the event is generated.让需要控制鼠标事件的 Forms 实现IMessageFilter ,因此这些 Forms 将事先知道哪些消息发送到哪个子控件,生成什么样的事件以及任何其他可能有用的细节,例如 Z486057FE06FDEAD492生成事件时的鼠标指针。 Mouse related messages are generated in any case, no matter whether you click on a Control that usually cannot get Focus, as the Form itself, or a Panel etc.在任何情况下都会生成与鼠标相关的消息,无论您是单击通常无法获得焦点的控件,例如表单本身还是面板等。
  • Your Forms also implement another Interface that just defines a public event that interested parties can subscribe to in order to receive fresh notifications about these events.您的 Forms 还实现了另一个接口,该接口仅定义了一个公共事件,相关方可以订阅该事件,以便接收有关这些事件的新通知。
    This also allows Control to detect whether the Parent Form is actually a Mouse Event notifier guy.这也允许 Control 检测父窗体是否实际上是鼠标事件通知程序。
  • When a Mouse event is notified (here, just the event generated when a WM_LBUTTONDOWN message is received), subscribers of the event(s) can decide to act upon it.当鼠标事件被通知时(这里,只是接收到WM_LBUTTONDOWN消息时生成的事件),事件的订阅者可以决定对其采取行动。 In your case, you can call the SelectNextControl() method of the Parent Form to set a different ActiveControl, only when your UserControl has the Focus and a MouseDown event is generate outside its bounds.在您的情况下,您可以调用父窗体的SelectNextControl()方法来设置不同的 ActiveControl,仅当您的 UserControl 具有焦点并且在其边界之外生成 MouseDown 事件时。

Call Application.AddMessageFilter() before the Form is constructed, passing the Form instance itself, since this Form implements the IMessageFilter interface.在构造 Form 之前调用Application.AddMessageFilter() ,传递 Form 实例本身,因为此 Form 实现了IMessageFilter接口。
Remove the message filter when the Form closes.表单关闭时删除消息过滤器。

Form side:表格面:
add a message filter to capture a mouse down event ( WM_LBUTTONDOWN ) and rise an event to notify the subscribers of the location where the event is generated and which is the Control that will be affected (passing its Handle).添加一个消息过滤器以捕获鼠标按下事件( WM_LBUTTONDOWN )并引发一个事件以通知订阅者该事件生成的位置以及将受到影响的控件(传递其句柄)。

public partial class SomeForm : Form, IMessageFilter, IMouseHandler {
    private const int WM_LBUTTONDOWN = 0x0201;
    public event EventHandler<MouseDownEventArgs> MouseEvent;

    public SomeForm() {
        Application.AddMessageFilter(this);
        this.FormClosed += (s, e) => Application.RemoveMessageFilter(this);

        InitializeComponent();
        // [...]
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_LBUTTONDOWN) {
            var pos = MousePosition;
            MouseEvent?.Invoke(this, new MouseDownEventArgs(m.HWnd, pos));
        }
        return false;
    }
}

UserControl part:用户控制部分:
if the Parent Form implements IMouseHandler , subscribe to its MouseEvent .如果父窗体实现IMouseHandler ,订阅它的MouseEvent When the event is raised, verify that the UserControl is the current ActiveControl (it contains the Focus) and that the Mouse event is generated outside its bounds.引发事件时,请验证 UserControl 是否为当前 ActiveControl(它包含焦点)以及是否在其边界之外生成了 Mouse 事件。 If these conditions are met, call the Parent Form's SelectNextControl() method to move the Focus elsewhere .如果满足这些条件,则调用父窗体的SelectNextControl()方法将焦点移至其他位置。

The bool m_MouseEventSubscribed is there because a UserControl may regenerate its Handle more than once in its life-time. bool m_MouseEventSubscribed之所以存在,是因为 UserControl 在其生命周期中可能不止一次地重新生成其句柄。

public partial class MyUserControl : UserControl
{
    private bool m_MouseEventSubscribed = false;

    public MyUserControl() => InitializeComponent();

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);

        var form = this.ParentForm;
        if (form != null && form is IMouseHandler && !m_MouseEventSubscribed) {
            m_MouseEventSubscribed = true;
            ((IMouseHandler)form).MouseEvent += (s, a) => {
                if (this.ContainsFocus && !this.ClientRectangle.Contains(PointToClient(a.Position))) {
                    form.SelectNextControl(this, true, true, false, true);
                }
            };
        }
    }
}

IMouseHandler Interface: IMouseHandler接口:

public interface IMouseHandler
{
    event EventHandler<MouseDownEventArgs> MouseEvent;
}

Custom EventArgs class:自定义EventArgs class:

using System;
using System.Drawing;

public class MouseDownEventArgs : EventArgs
{
    public MouseDownEventArgs() { }
    public MouseDownEventArgs(IntPtr hWnd, Point point)
    {
        this.ControlHandle = hWnd;
        this.Position = point;
    }
    public IntPtr ControlHandle { get; }
    public Point Position { get; }
}

When you click on controls, the click event is eaten by them and doesn't go to the form itself.当您单击控件时,单击事件被它们吃掉并且不会 go 到表单本身。 To notify the form that one of its controls was clicked, use something like this:要通知表单其中一个控件被单击,请使用以下内容:

 private void control_Click(object sender, EventArgs e)
    {
        // you control click code here
        this.OnClick(new EventArgs());
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM