【问题标题】:Winforms: How to emulate AutoClose with a ToolStripDropDownWinforms:如何使用 ToolStripDropDown 模拟 AutoClose
【发布时间】:2014-04-02 06:29:17
【问题描述】:

我目前正在编写一个组合框,用我自己的替换建议下拉列表。为此,我创建了一个 ToolStripDropDown,我用一个列表框填充它。当用户在组合框文本字段中键入文本时,此列表框会更新。

我的问题是,如果我将 ToolStripDropDown 的 AutoClose 属性设置为 true,似乎即使文本字段具有焦点,ToolStripDropDown 也会“窃取”很多消息,包括键盘消息。

如果我将 AutoClose 属性设置为 false,则一切正常。但下拉菜单不会关闭,例如,如果用户在组合之外点击。

我想知道自己做“自动关闭”,但我不确定如何实现它。关于如何做到这一点的任何想法?

【问题讨论】:

  • hm 也许你也可以控制焦点或为你的组合框制作一个“KeyPreview”(就像winforms一样)
  • 我试过了,但它不起作用。我还尝试捕获所有发送到下拉列表的 WM_KEYDOWN、WM_KEYUP、WM_CHAR 消息并将它们发送回组合的编辑控件(使用 SendMessage),但它也不起作用。
  • 一个非常简单但可能很脏的解决方案:ComboBox.LostFocus -> Toolstripdropdown.Close() 可能有效,但我还没有测试过。我认为一个简单的“自动关闭”
  • 我已经在处理 LostFocus 消息了。如果我点击组合外的父表单,我不会收到任何消息。
  • 嗯,我已经测试过了,它在这里工作。你有一些代码如何处理 ComboBox 的 LostFocus 吗?

标签: c# winforms combobox toolstripdropdown


【解决方案1】:

我找到了解决方案。查看 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;
        }
}

【讨论】:

  • 所以 ToolStripManager 将消息赶走? O.o
  • 是的,消息处理的一部分将所有键盘输入重定向到活动下拉列表(ToolStripManager.PreFilterMessage -> case WM_KEYDOWN:case WM_KEYUP:etc...)
  • 嗨,Serge,我遇到了同样的问题,尽管我正在使用嵌入式 datagridview 实现自定义多列组合框。我是否有机会看到您的完整代码重新评分此自动建议窗口?...提前致谢
  • 没关系,我得到了它的工作,感谢精彩的编码。来自我的 +1。
猜你喜欢
  • 2018-10-04
  • 2012-11-29
  • 2012-01-29
  • 1970-01-01
  • 2011-01-15
  • 2011-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多