Chimera
Chimera

Reputation: 6038

How to Leave ToolStripMenu Open After Clicking an Item

I'm creating a ToolStripMenu shown below that is supposed to allow the user to interact with the items "XML" and "Non XML" as though they are regular check boxes on a form. However, when one item is checked/unchecked the menu closes. How can I allow an item to be checked/unchecked without closing the menu? Or is there a different standard method of achieving the same behavior?

enter image description here

So what I want is to be able to click on "Non XML", show a check box and leave the menu open. The idea is that the last menu item will be "Done" and when it's clicked the "G2S" sub items will remain open but the "Display" sub items ( XML, Non XML ) will close.

Any ideas?

Note: I am aware that this is likely not the best user interface design. I'd like to know however how this could be accomplished just to gain some technical knowledge about handling menus.

Upvotes: 5

Views: 9500

Answers (6)

user10216583
user10216583

Reputation:

If someone is still interested, here is a vb solution:

1) For the parent tool strip menu item, add the following handler in the form's constructor:

AddHandler ParentTSMI.DropDown.Closing, AddressOf onDropDownClosing

2) The handler:

Private Sub onDropDownClosing(sender As Object, e As ToolStripDropDownClosingEventArgs)
    If e.CloseReason = ToolStripDropDownCloseReason.ItemClicked Then
        e.Cancel = True
    End If
End Sub

That's it all.

Don't forget to remove the handler (RemoveHandler) when you close the form.

Upvotes: 1

Peter
Peter

Reputation: 31

Here's a useful extension that requires user to click outside of menu item + dropdowns to close.

    public static void KeepOpenOnDropdownCheck (this ToolStripMenuItem ctl)
    {
        foreach (var item in ctl.DropDownItems.OfType<ToolStripMenuItem>())
        {
            item.MouseEnter += (o, e) => ctl.DropDown.AutoClose = false;
            item.MouseLeave += (o, e) => ctl.DropDown.AutoClose = true;
        }

    }

Upvotes: 3

Mark Ainsworth
Mark Ainsworth

Reputation: 857

I used a combination of Neolisk's and Chimera's answers to allow deletion of multiple leaf items from a treeview. My solution is below

Note: the following Items created at design time are used: TreePromotions (TreeView) menuVendorSection (Context Menu Strip) removeMultipleItemsToolStripMenuItem (DropDown of menuVendorSection)

 private void removeMultipleItemsToolStripMenuItem_MouseHover(object sender, EventArgs e)
    {
        removeMultipleItemsToolStripMenuItem.DropDownItems.Clear();  
        ToolStripMenuItem detailMenuItem;
        TreeNode vendorSectionNode = treePromotions.SelectedNode;
        for (int vsn = 0; vsn < vendorSectionNode.Nodes.Count; vsn++)
        {
            //add checkbox item
            detailMenuItem = new ToolStripMenuItem(vendorSectionNode.Nodes[vsn].Text);
            detailMenuItem.Tag = vendorSectionNode.Nodes[vsn].Tag;
            detailMenuItem.CheckOnClick = true;
            removeMultipleItemsToolStripMenuItem.DropDownItems.Add(detailMenuItem);

        }
        //add action buttons
        Button buttonDeleteMultiple = new Button();
        buttonDeleteMultiple.Text = "Remove Checked Items";
        ToolStripControlHost buttonHost = new ToolStripControlHost(buttonDeleteMultiple);
        buttonDeleteMultiple.Click += new EventHandler(buttonDeleteMultiple_Click);
        removeMultipleItemsToolStripMenuItem.DropDownItems.Add(buttonHost);

        Button buttonCancelMultipleDelete = new Button();
        buttonCancelMultipleDelete.Text = "CANCEL";
        buttonHost = new ToolStripControlHost(buttonCancelMultipleDelete);
        buttonCancelMultipleDelete.Click += new EventHandler(buttonCancelMultipleDelete_Click);
        removeMultipleItemsToolStripMenuItem.DropDownItems.Add(buttonHost);



        removeMultipleItemsToolStripMenuItem.DropDown.AutoClose = false;
        menuVendorSection.AutoClose = false;





    }

    private void buttonDeleteMultiple_Click(object sender, EventArgs e)
    {

        //delete items
        for (int dmi = 0; dmi < removeAllItemsToolStripMenuItem.DropDownItems.Count - 2; dmi++) //do not include buttons
        {
            ((Detail)removeAllItemsToolStripMenuItem.DropDownItems[dmi].Tag).Delete(); //deletes item from database
        }
        //rebuild leaf
        treePromotions.SelectedNode.Nodes.Clear();
        addItemNodes(treePromotions.SelectedNode);  //builds leaf nodes from database

        //close menus
        removeMultipleItemsToolStripMenuItem.DropDown.Close();
        menuVendorSection.AutoClose = true;
        menuVendorSection.Close();
    }

    private void buttonCancelMultipleDelete_Click(object sender, EventArgs e)
    {
        //just close menus
        removeMultipleItemsToolStripMenuItem.DropDown.Close();
        menuVendorSection.AutoClose = true;
        menuVendorSection.Close();
    }

And here is what it looks like

Upvotes: 1

autodidact
autodidact

Reputation: 17

The original solution will work with the use of mouse events.

On mouse enter event:

parent.dropdown.autoclose = false;

on mouse leave event:

parent.dropdown.autoclose = true;

The only catch is if the user access the menu items by other means than a mouse.

Upvotes: 0

Victor Zakharov
Victor Zakharov

Reputation: 26454

Interesting concept is described in this thread on Stackoverflow:

Here is the essence of the accepted answer:

ParentMenu.DropDown.AutoClose = false;

It does exactly what you are asking for - prevent menu from closing when subitem is clicked.

Upvotes: 5

Chimera
Chimera

Reputation: 6038

Posted in case somebody finds it helpful.

Instead of trying to do exactly what I had originally intended, I've come up with the following:

1- Use a ContextMenuStrip
2- When the user clicks on the ToolStripMenu item I display the ContextMenuStrip at a location near the menu item as shown below: ( note the positioning still needs adjusting )

enter image description here

To get this working I build the ContextMenuStrip in code at run-time so that the items in the ContextMenuStrip can be build dynamically based on the situation.

Code snippets:

Show the ContextMenuStrip when the menu item is clicked:

private void filterToolStripMenuItem_Click(object sender, EventArgs e)
{
    contextMenuStrip1.Show(this, 180, 20);
}

Build the ContextMenuStrip:

    if (protInfo.Name == "QCOM" )
    {
        BroadCast = new CheckBox();
        BroadCast.Text = "Date/Time Broadcast";
        BroadCast.Checked = FlagSet(CurrentFilter, (Byte)Filter.DateTimeBC);
        ToolStripControlHost Ch1 = new ToolStripControlHost(BroadCast);

        GenPoll = new CheckBox();
        GenPoll.Text = "Status Poll";
        GenPoll.Checked = FlagSet(CurrentFilter, (Byte)Filter.GenStatusPoll);
        ToolStripControlHost Ch2 = new ToolStripControlHost(GenPoll);

        GenPollResp = new CheckBox();
        GenPollResp.Text = "Status Poll Response";
        GenPollResp.Checked = FlagSet(CurrentFilter, (Byte)Filter.GenStatusResponse);
        ToolStripControlHost Ch3 = new ToolStripControlHost(GenPollResp);

        Button btnDone = new Button();
        btnDone.Text = "Done";
        ToolStripControlHost Ch4 = new ToolStripControlHost(btnDone);
        btnDone.Click += new EventHandler(btnDone_Click);

        contextMenuStrip1.Items.Clear();
        contextMenuStrip1.Items.Add(Ch1);
        contextMenuStrip1.Items.Add(Ch2);
        contextMenuStrip1.Items.Add(Ch3);
        contextMenuStrip1.Items.Add(Ch4);
        contextMenuStrip1.Enabled = true;
        filterToolStripMenuItem.Enabled = true;
    }
    else
    {
        filterToolStripMenuItem.Enabled = false;
    }

This may not be the best user interface design, but it seems to work.

Upvotes: 2

Related Questions