繁体   English   中英

WinForms 用户控件具有导致 ToolStripDropDown 自动关闭的 ComboBox

[英]WinForms User Control has ComboBox that causes ToolStripDropDown to auto-close

我有一个自定义的 WinForms 用户控件,它看起来像一个组合框,但打开了一个 ToolStripDropDown,其中包含另一个名为 NumericFilterPanel 的自定义用户控件,它有一个复选框、一个组合框和一个文本框。

在此处输入图片说明

问题在于,当用户单击选择下拉控件中嵌入的组合框的选项时,会导致父下拉列表隐藏。

我已经设置了 ToolStripDropDown.AutoClose = false,这解决了原来的问题,但是现在我很难检测下拉列表失去焦点的所有情况,例如当用户单击父窗体或切换程序时。 有时下拉菜单仍然可见且位于最顶部。

有没有办法保持 AutoClose = true 并防止嵌入的组合框关闭父下拉菜单,或者有没有办法始终检测下拉菜单何时失去焦点以便我可以手动关闭它?

  using System;
  using System.Drawing;
  using System.Windows.Forms;

  namespace mviWinControls
  {
    public partial class NumericRangeDropDown : UserControl
    {
      private const int ARROW_HEIGHT = 4;
      private Brush arrowBrush = new SolidBrush(Color.FromArgb(77, 97, 133));

      private ToolStripDropDown _dropdown;
      private ToolStripControlHost _host;
      private NumericFilterPanel _filter;

      public NumericRangeDropDown()
      {
        InitializeComponent();

        _filter = new NumericFilterPanel();
        _filter.DropDown = this;

        _host = new ToolStripControlHost(_filter);
        _host.Margin = Padding.Empty;
        _host.Padding = Padding.Empty;

        _dropdown = new ToolStripDropDown();
        _dropdown.Margin = Padding.Empty;
        _dropdown.Padding = Padding.Empty;
        _dropdown.AutoClose = false;  // Use this because panel has a combobox.  https://social.msdn.microsoft.com/Forums/windows/en-US/dd95b982-820e-4807-8a1f-79c74acab3f8/two-problems-toolstripdropdown?forum=winforms
        _dropdown.Items.Add(_host);
        _dropdown.Leave += new System.EventHandler(this.DropDown_Leave);

        this.Leave += new System.EventHandler(this.DropDown_Leave);
      }

      /// <summary> 
      /// Clean up any resources being used.
      /// </summary>
      /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
      protected override void Dispose(bool disposing)
      {
        if (disposing)
        {
          if (components != null) components.Dispose();
          if (_dropdown != null) _dropdown.Dispose();
        }
        base.Dispose(disposing);
      }

      public override string Text
      {
        get
        {
          return base.Text;
        }
        set
        {
          base.Text = value;
          _filter.SetValue(value);
        }
      }

      protected override void OnPaint(PaintEventArgs e)
      {
        //base.OnPaint(e);
        TextBox _txtDraw = new TextBox();

        _txtDraw.Width = this.Width;

        using (Bitmap bmp = new Bitmap(_txtDraw.Width, _txtDraw.Height))
        {
          _txtDraw.DrawToBitmap(bmp, new Rectangle(0, 0, _txtDraw.Width, _txtDraw.Height));
          e.Graphics.DrawImage(bmp, 0, 0);
        }

        StringFormat format = new StringFormat();
        format.Alignment = StringAlignment.Near;
        format.FormatFlags = StringFormatFlags.NoWrap;
        format.LineAlignment = StringAlignment.Center;

        using (Brush b = new SolidBrush(this.ForeColor))
          e.Graphics.DrawString(this.Text, this.Font, b, this.DisplayRectangle, format);

        Point[] arrowPoints = new Point[] { new Point(this.Width - ARROW_HEIGHT * 3 - 2, (this.Height - ARROW_HEIGHT) / 2),
                                            new Point(this.Width - ARROW_HEIGHT + 1 - 2, (this.Height - ARROW_HEIGHT) / 2),
                                            new Point(this.Width - ARROW_HEIGHT * 2 - 2, this.Height - (this.Height - ARROW_HEIGHT) / 2) };

        e.Graphics.FillPolygon(arrowBrush, arrowPoints );

      }

      private void DropDown_Leave(object sender, EventArgs e)
      {
        HideDropDown();
        this.Text = _filter.SummaryText();
      }

      private void NumericRangeDropDown_Click(object sender, EventArgs e)
      {
        if (_dropdown.Visible)
          HideDropDown();
        else
          ShowDropDown();
      }

      public void ShowDropDown()
      {
        _dropdown.Show(this, new Point(0, this.Height), ToolStripDropDownDirection.Default);
        _dropdown.BringToFront();
        //_dropdown.Focus();
        _filter.Select();
        _filter.Focus();
      }

      public void HideDropDown()
      {
        _dropdown.Close();
        this.Invalidate();
      }

    }
  }

这是一个组合框,可以自动为您禁用和启用主机控件上的AutoClose属性。

来源(我在示例中将其修改为组合框而不是DatePicker): http : //www.queasy.me/programming/questions/13919634/tool+strip+toolstripdropdownbutton+close+and+lose+window+focus

public partial class CComboBox : ComboBox
{
    private bool savedAutoClose;

    public CComboBox()
    {
        InitializeComponent();
    }

    protected override void OnDropDownClosed(EventArgs e)
    {
        if (this.Parent != null)
        {
            var dropDownHost = this.Parent.Parent as ToolStripDropDown; // recursive instead?
            if (dropDownHost != null)
                dropDownHost.AutoClose = savedAutoClose; // restore the parent's AutoClose preference
        }

        base.OnDropDownClosed(e);
    }

    protected override void OnDropDown(EventArgs e)
    {
        if (this.Parent != null)
        {
            var dropDownHost = this.Parent.Parent as ToolStripDropDown; // recursive instead?
            if (dropDownHost != null)
            {
                savedAutoClose = dropDownHost.AutoClose;
                // ensure that our parent doesn't close while the calendar is open
                dropDownHost.AutoClose = false;
            }
        }
        base.OnDropDown(e);
    }
}

仔细查看源代码后,错误(并且是错误)在于ToolStripManager设置消息过滤器以捕获活动ToolStrip之外的鼠标点击,检查点击是否在边界内的一个子窗口。

问题是它使用activeToolStrip.ClientRectangle来验证这一点,并且不检查被单击的窗口的子窗口。 ComboBox的情况下,下拉菜单是一个单独的子窗口,它浮动在所有内容之上,如果下拉菜单很大,实际上可能超出主组合窗口的范围。

相关行是:

if (!activeToolStrip.ClientRectangle.Contains(pt.x, pt.y)) {

我找到了另一种在下拉菜单打开时暂时禁用自动关闭的解决方案。

理想情况下,您应该在ToolStripComboBox中使用ToolStrip而不仅仅是一个裸的ComboBox 但是,如果您只想给我们一个裸机,您可以添加事件来调用相关的私有方法来暂停和恢复消息过滤器。

static class ToolStripComboBoxFilter
{
    private static Action SuspendMenuMode = (Action) typeof(ToolStripManager)
        .GetNestedType("ModalMenuFilter", BindingFlags.NonPublic)
        .GetMethod(nameof(SuspendMenuMode), BindingFlags.NonPublic | BindingFlags.Static)
        .CreateDelegate(typeof(Action));

    private static Action ResumeMenuMode = (Action)typeof(ToolStripManager)
        .GetNestedType("ModalMenuFilter", BindingFlags.NonPublic)
        .GetMethod(nameof(ResumeMenuMode), BindingFlags.NonPublic | BindingFlags.Static)
        .CreateDelegate(typeof(Action));

    public static void AddToolStripFilterEvents(this ComboBox combo)
    {
        combo.DropDown += OnDropDown;
        combo.DropDownClosed += OnDropDownClosed;
    }

    private static void OnDropDown(object sender, EventArgs e)
    {
        SuspendMenuMode();
    }

    private static void OnDropDownClosed(object sender, EventArgs e)
    {
        ResumeMenuMode();
    }
}

你可以像这样使用它

myComboBox.AddToolStripFilterEvents();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM