我目前正在编写一个ComboBox,我用我自己的建议下拉列表替换了建议下拉列表。为此,我创建了一个ToolStripDropDown,我用一个列表框填充它。当用户在组合框文本字段中键入文本时,此列表框将更新。
我的问题是,如果我将 ToolStripDropDown 的 AutoClose 属性设置为 true,似乎即使文本字段具有焦点,ToolStripDropDown 也会"窃取"大量消息,包括键盘消息。
如果我将自动关闭属性设置为 false,则一切正常。但是,例如,如果用户在组合外部单击,则下拉列表不会关闭。
我想做我自己的"自动关闭",但我不确定如何实现它。知道如何做到这一点吗?
我找到了解决方案。查看 ToolStripManager 代码(感谢 ReSharper!),我发现当 AutoClose 设置为 true 时,管理器正在监视应用程序消息,以便检测用户何时单击下拉列表之外。我通过以下方式调整了他们的代码:
class MyComboBox : ComboBox, IMessageFilter
{
private ToolStripDropDown m_dropDown;
MyComboBox()
{
...
Application.AddMessageFilter(this);
...
}
protected override void Dispose(bool disposing)
{
...
Application.RemoveMessageFilter(this);
base.Dispose(disposing);
}
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_MBUTTONDOWN = 0x0207;
private const int WM_NCLBUTTONDOWN = 0x00A1;
private const int WM_NCRBUTTONDOWN = 0x00A4;
private const int WM_NCMBUTTONDOWN = 0x00A7;
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref Point pt, int cPoints);
public bool PreFilterMessage(ref Message m)
{
if (m_dropDown.Visible)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_NCLBUTTONDOWN:
case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN:
//
// When a mouse button is pressed, we should determine if it is within the client coordinates
// of the active dropdown. If not, we should dismiss it.
//
int i = unchecked((int)(long)m.LParam);
short x = (short)(i & 0xFFFF);
short y = (short)((i >> 16) & 0xffff);
Point pt = new Point(x, y);
MapWindowPoints(m.HWnd, m_dropDown.Handle, ref pt, 1);
if (!m_dropDown.ClientRectangle.Contains(pt))
{
// the user has clicked outside the dropdown
pt = new Point(x, y);
MapWindowPoints(m.HWnd, Handle, ref pt, 1);
if (!ClientRectangle.Contains(pt))
{
// the user has clicked outside the combo
hideDropDown();
}
}
break;
}
}
return false;
}
}