我正在创建一个自定义下拉框,我想在下拉框外单击鼠标时进行注册,以便隐藏它。是否可以检测到控件外的点击?或者我应该在包含的表格上制作一些机制,并在任何下拉框打开时检查鼠标点击?
答案 0 :(得分:13)
所以我终于明白,当用户点击之外,你只希望它关闭。在这种情况下,Leave
event应该可以正常工作...出于某种原因,只要他们将鼠标移到您的自定义下拉列表之外,我就会得到您希望它关闭的印象。只要您的控件失去焦点,就会引发Leave
事件,如果用户点击了其他内容,则当他们点击的内容获得焦点时,它肯定会失去焦点。
文档还说,此事件会根据需要在控制链中上下级联:
Enter
和Leave
事件是分层的,将在父链上下级联,直到达到适当的控制。例如,假设您有一个带有两个GroupBox控件的Form,并且每个GroupBox控件都有一个TextBox控件。当插入符号从一个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);
}
}
是的,这最后一部分感觉像是黑客。正如其他人所提到的,更好的解决方案是使用SetCapture
function指示Windows在UserControl窗口上捕获鼠标。控件的Capture
property提供了一种更简单的方法来执行相同的操作。
答案 1 :(得分:2)
从技术上讲,您需要p / invoke SetCapture()才能接收您无法控制的点击事件。
但是在你的情况下,正如@Martin建议的那样,处理Leave事件就足够了。
编辑:在寻找SetCapture()
的使用示例时,我遇到了Control.Capture属性,我不知道。使用该属性意味着你不必调用任何东西,这在我的书中总是一件好事。
因此,在显示下拉列表时,您必须将Capture
设置为true
,然后确定鼠标指针是否位于Click事件处理程序中的控件内部,如果不是,将Capture
设置为false
并关闭下拉列表。
答案 2 :(得分:2)
处理表单的MouseDown
事件,或覆盖表单的OnMouseDown
方法:
enter code here
然后:
protected override void OnMouseDown(MouseEventArgs e)
{
if (!theListBox.Bounds.Contains(e.Location))
{
theListBox.Visible = false;
}
}
旧的System.Drawing.Rectangle
包含方法可用于指示是否
一个点包含在一个矩形内。 Control的Bounds属性是
由控件边缘定义的外部Rectangle
。那个地点
MouseEventArgs
的属性是相对于Control的Point
收到MouseDown
事件。表单中控件的Bounds属性是
相对于表格。
答案 3 :(得分:0)
您可能正在寻找休假活动:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave.aspx
当输入焦点离开控件时发生离开。
答案 4 :(得分:0)
我只想分享这个。这样做可能不是一个好方法,但是看起来它适用于在假的“ MouseLeave”上关闭的下拉面板,我试图将其隐藏在Panel 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
}
}
答案 5 :(得分:-1)
我自己就是这样做的,这就是我做到的。
打开下拉列表时,在控件的父窗体上注册一个click事件:
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);
我想我现在已经知道了,这应该涵盖所有基础。如果我遗失了什么,请告诉我。
答案 6 :(得分:-1)
如果你有表单,你可以像这样简单地使用 Deactivate
事件:
protected override void OnDeactivate(EventArgs e)
{
this.Dispose();
}