ToolStripDropDown 在底层控制之前捕获 MouseeDown

ToolStripDropDown catches MouseeDown before underlying control

我创建了自己的类似 ComboBox 的控件,其中下拉部分包含一棵树。我见过那些使用普通 ComboBox 并覆盖 WndProc 的解决方案,但尽管有很多很多代码,但总会出现一些奇怪的行为。所以我决定让它变得简单:只有一个带有 ToolStripDropDown/ToolStripControlHost 的标签,当鼠标落在标签上时会打开该标签。缺少的 ComboBox 三角形没有坏处。

除一件小事外,一切都完美无缺:就像普通的 ComboBox 一样,我希望在再次单击标签时隐藏下拉菜单。但是当我点击它时,下拉菜单会隐藏一瞬间,然后再次出现。如果我在标签外单击,下拉菜单就会隐藏,就像它应该的那样。

public class RepoNodeComboBox: Label
{
    RepoTreeView repoTreeView;
    ToolStripControlHost treeViewHost;
    ToolStripDropDown dropDown;
    bool isDropDownOpen;

    public int DropDownHeight;

    public RepoNodeComboBox()
    {
        repoTreeView = new RepoTreeView();
        repoTreeView.BorderStyle = BorderStyle.None;
        repoTreeView.LabelEdit = false;

        treeViewHost = new ToolStripControlHost(repoTreeView);
        treeViewHost.Margin = Padding.Empty;
        treeViewHost.Padding = Padding.Empty;
        treeViewHost.AutoSize = false;

        dropDown = new ToolStripDropDown();
        dropDown.CanOverflow = true;
        dropDown.AutoClose = true;
        dropDown.DropShadowEnabled = true;
        dropDown.Items.Add(treeViewHost);
        dropDown.Closing += dropDownClosing;

        TextAlign = ContentAlignment.MiddleLeft;
        BackColor = SystemColors.Window;
        BorderStyle = BorderStyle.FixedSingle;

        DropDownHeight = 400;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing) 
        {
            if (dropDown != null) 
            {
                dropDown.Dispose();
                dropDown = null;
            }
        }
        base.Dispose(disposing);
    }

    // when mouse goes down on the label, this is executed first            
    void dropDownClosing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        // just to test if I can get more out of it than with dropdown.Visible
        isDropDownOpen = false; 
    }

    // this is subsidiary to the Closing event of the dropdown
    protected override void OnMouseDown(MouseEventArgs e)
    {
        if (dropDown != null) 
        {
            if (isDropDownOpen)
            {
                dropDown.Hide();
            }
            else
            {
                repoTreeView.Size = new Size(Width, DropDownHeight);
                treeViewHost.Width = Width;
                treeViewHost.Height = DropDownHeight;
                dropDown.Show(this, 0, Height);
                isDropDownOpen = true;
            }
        }

        base.OnMouseDown(e);
    }
}

据我所知(断点),下拉菜单首先捕获 MOUSEDOWN 事件以关闭自身。只有在那之后我的标签才通过 MOUSEDOWN 事件传递,并且由于它看到下拉菜单已关闭,它认为标签已被第一次点击 - 并再次打开下拉菜单。

所以标签似乎没有机会知道 MOUSEDOWN 是否是关闭下拉项的结果。我可以每隔一个事件打开它,但这不需要发生其他关闭事件。

有什么方法可以确保打开的下拉项即使在我点击标签时也能立即关闭?

尝试在浮动主机关闭时检查鼠标位置:

void dropDownClosing(object sender, CancelEventArgs e) {
  isDropDownOpen = this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition));
}

在 MouseDown 代码中,确保在 true 块中将 isDropDownOpen 设置为 false:

protected override void OnMouseDown(MouseEventArgs e) {
  if (dropDown != null) {
    if (isDropDownOpen) {
      isDropDownOpen = false;
      dropDown.Hide();
    } else {
      isDropDownOpen = true;
      repoTreeView.Size = new Size(Width, DropDownHeight);
      treeViewHost.Width = Width;
      treeViewHost.Height = DropDownHeight;
      dropDown.Show(this, 0, Height);
    }
  }
  base.OnMouseDown(e);
}