[英]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:您可以使用通用消息处理程序/调度程序:
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.