所以我终于明白,您只想在用户点击它之外的区域时关闭它。在这种情况下,Leave
事件应该可以很好地工作... 不知何故,我有印象你希望它每当他们将鼠标移动到自定义下拉菜单之外时就关闭。当控件失去焦点时,会引发Leave
事件,如果用户单击其他东西,控件就会失去焦点,因为他们单击的事物获得了焦点。
文档还指出,此事件会根据需要向上和向下级联控件链:
Enter
和Leave
事件是分层的,并将级联到父级链上,直到到达适当的控件。例如,假设您有一个包含两个GroupBox控件的窗体,每个GroupBox控件都有一个TextBox控件。当光标从一个TextBox移动到另一个时,将为TextBox和GroupBox引发Leave
事件,并为另一个GroupBox和TextBox引发Enter
事件。
覆盖您的UserControl的OnLeave
方法是处理此问题的最佳方法:
protected override void OnLeave(EventArgs e)
{
// Call the base class
base.OnLeave(e);
// When this control loses the focus, close it
this.Hide();
}
为了进行测试,我创建了一个表单,可以通过命令显示下拉UserControl:
public partial class Form1 : Form
{
private UserControl1 customDropDown;
public Form1()
{
InitializeComponent();
// Create the user control
customDropDown = new UserControl1();
// Add it to the form's Controls collection
Controls.Add(customDropDown);
customDropDown.Hide();
}
private void button1_Click(object sender, EventArgs e)
{
// Display the user control
customDropDown.Show();
customDropDown.BringToFront(); // display in front of other controls
customDropDown.Select(); // make sure it gets the focus
}
}
以上代码完美运行,但是有一个问题:如果用户单击表单的空白区域,UserControl不会关闭。为什么呢?因为表单本身不想获得焦点,只有控件能够获得焦点,而我们没有单击控件。并且由于没有其他控件窃取焦点,Leave
事件从未被触发,这意味着UserControl不知道它应该关闭自己。
如果您需要在用户单击表单的空白区域时关闭UserControl,则需要对此进行特殊处理。由于您只关心单击,所以可以处理表单的Click
事件,并将焦点设置为不同的控件:
protected override void OnClick(EventArgs e)
{
// Call the base class
base.OnClick(e);
// See if our custom drop-down is visible
if (customDropDown.Visible)
{
// Set the focus to a different control on the form,
// which will force the drop-down to close
this.SelectNextControl(customDropDown, true, true, true, true);
}
}
是的,这个最后一部分感觉像是一个hack。更好的解决方案,正如其他人所提到的,是使用SetCapture
函数指示Windows捕获鼠标在您的UserControl窗口上移动。控件的Capture
属性提供了一种更简单的方法来完成相同的事情。
SetCapture()
的用法示例时,我遇到了 Control.Capture 属性,这是我不知道的。使用该属性意味着您无需进行 p/invoke 操作,这对我来说总是一件好事。Capture
设置为 true
,然后在单击事件处理程序中确定鼠标指针是否位于控件内部,如果不是,则将 Capture
设置为 false
并关闭下拉列表。
更新:
您还可以使用Control.Focused属性来确定在使用键盘或鼠标时控件是否获得或失去了焦点,而不是使用与MSDN Capture页面中提供的相同示例的Capture
。
处理表单的 MouseDown
事件,或者重写表单的 OnMouseDown
方法:
enter code here
那么:
protected override void OnMouseDown(MouseEventArgs e)
{
if (!theListBox.Bounds.Contains(e.Location))
{
theListBox.Visible = false;
}
}
Contains方法旧的System.Drawing.Rectangle
可以用于指示一个点是否包含在矩形内。控件的Bounds属性是由控件边缘定义的外部Rectangle
。 MouseEventArgs
的Location属性是相对于接收MouseDown
事件的控件而言的点。在窗体中,控件的Bounds属性是相对于窗体而言的。
你可能正在寻找离开事件:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave.aspx
当输入焦点离开控件时,会发生离开事件。
我只是想分享一下。这可能不是一个好的方法,但看起来它对于关闭虚假“MouseLeave”的下拉面板有效。我尝试在面板MouseLeave时隐藏它,但它不起作用,因为从面板到按钮移动会离开面板,因为按钮不是面板本身。可能有更好的方法,但我分享这个是因为我花了大约7个小时来找出如何使其工作。感谢@FTheGodfather
但如果鼠标移动到面板上,它只适用于在表单上移动。
private void click_to_show_Panel_button_MouseDown(object sender, MouseEventArgs e)
{
item_panel1.Visible = true; //Menu Panel
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!item_panel1.Bounds.Contains(e.Location))
{
item_panel1.Visible = false; // Menu panel
}
}
protected override void OnDeactivate(EventArgs e)
{
this.Dispose();
}
我自己做过这个,以下是我的做法。
当下拉菜单被打开时,在控件的父表单上注册一个点击事件:
this.Form.Click += new EventHandler(CloseDropDown);
但这只能带你走一半的路。当当前窗口被取消激活时,您可能也希望关闭下拉列表。最可靠的检测方法对我来说是通过定时器检查哪个窗口当前处于活动状态:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
并且
var timer = new Timer();
timer.Interval = 100;
timer.Tick += (sender, args) =>
{
IntPtr f = GetForegroundWindow();
if (this.Form == null || f != this.Form.Handle)
{
CloseDropDown();
}
};
this.Form.LocationChanged += new EventHandler(CloseDropDown);
this.Form.SizeChanged += new EventHandler(CloseDropDown);
别忘了在CloseDropDown方法中注销所有这些事件 :)
编辑:
我忘了,你还应该在控件上注册Leave事件,以查看是否有其他控件被激活/点击:
this.Leave += new EventHandler(CloseDropDown);
我想我现在明白了,这应该涵盖了所有的基础。如果我漏掉了什么,请告诉我。