oliver
oliver

Reputation: 2891

ToolStripDropDown catches MouseeDown before underlying control

I have created my own ComboBox-like control where the dropdown part contains a tree. I have seen those solutions using an ordinary ComboBox and overwriting the WndProc, but there was always some odd behavior despite lots and lots of code. So I decided to make it simple: just a label with a ToolStripDropDown/ToolStripControlHost that is opened when mouse goes down on the label. The missing ComboBox triangle doesn't hurt.

Everything works perfectly, except one tiny thing: like with the stock ComboBox I would like the dropdown to hide when I click on the label again. But when I click on it, the dropdown hides for a split second, just to appear again. If I click outside the label, the dropdown just hides, like it should be.

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);
    }
}

As far as I can see (breakpoints), the dropdown catches the MOUSEDOWN event first in order to close itself. Only after that my label gets passed through the MOUSEDOWN event, and since it sees the dropdown is closed, it thinks the label has been clicked like for the first time - and opens the dropdown again.

So it seems the label has no chance of knowing if the MOUSEDOWN was the result of closing the dropdown item. I could open it every other event, but that would require no other closing events to happen.

Is there any way to make sure that an open dropdown item just closes even if I click on the label?

Upvotes: 1

Views: 102

Answers (1)

LarsTech
LarsTech

Reputation: 81610

Try checking for the Mouse position when the floating host closes:

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

On the MouseDown code, make sure to set isDropDownOpen to false in the true block:

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);
}

Upvotes: 1

Related Questions