Search code examples
c#winformstreeviewcontextmenustriptoolstripmenu

Is there a way to know which TreeNode is right clicked after one of its ContextMenuStrip items is left clicked and the item's CheckedState changes?


Update on 5/10/2018. orhtej2 suggested that my question might be a dup of Determine what control the ContextMenuStrip was used on. It is almost a dup, but my scenario has one significant difference. See my modified question and my answer for details.

See my code below. My goal is more cleanly determine which TreeNode is right-clicked after one of its ContextMenuItems is left clicked.

As it is now, when I right-click one of the two child nodes, the if statement in the TreeView1_NodeMouseClick loads the clicked TreeNode into the global treeViewClickedNode TreeNode object. Then, when I left click one of the two contextMenuStripChildNode ToolStripMenuItem, the DocumentActionToolStripMenuItem_CheckStateChanged method is fired. I can then examine the check state. If its checked, I can then do something to the treeViewClickedNode TreeNode.

My question: Is there a cleaner way to determine which TreeNode is right clicked after one of its ContextMenuStrip items is left clicked, i.e., is there a way to do away with the global variable treeViewClickedNode?

Note: The only thing I did in the designer was to place treeview1 on Form1, dock it to Form1 and set 'treeview1' NodeMouseClick to TreeView1_NodeMouseClick

using System;
using System.Windows.Forms;

namespace WindowsFormsApp_Scratch
{
    public partial class Form1 : Form
    {
        TreeNode treeViewClickedNode;
        ContextMenu mnu = new ContextMenu();

        public Form1()        
        {
            InitializeComponent();

            // Create the root node.
            TreeNode treeNodeRoot = new TreeNode("Documents");

            // Add the root node to the TreeView.
            treeView1.Nodes.Add(treeNodeRoot);

            //Create and add child 2 nodes each with a two item ContextMenuStrip.
            string[] childNodeLabels = { "document1.docx", "document2.docx"};
            string[] contextItemLabels = { "Action A", "Action B" };

            foreach (String childNodeLabel in childNodeLabels)
            {
                TreeNode treeNode = treeNodeRoot.Nodes.Add(childNodeLabel);

                // Create a ContextMenuStrip for this child node.
                ContextMenuStrip contextMenuStripChildNode = new ContextMenuStrip
                {
                    ShowCheckMargin = true,
                    ShowImageMargin = false
                };

                foreach (String contextItemLabel in contextItemLabels)
                {
                    //Create a menu item.
                    ToolStripMenuItem action = new ToolStripMenuItem(contextItemLabel, null, DocumentActionToolStripMenuItem_CheckStateChanged)
                    {
                        CheckOnClick = true
                    };

                    contextMenuStripChildNode.Items.Add(action);
                }

                treeNode.ContextMenuStrip = contextMenuStripChildNode;
            }


        private void TreeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            TreeView t = (TreeView)sender;
            //Force the node that was right-clicked to be selected
            t.SelectedNode = t.GetNodeAt(e.X, e.Y);

            if (e.Button == MouseButtons.Right)
            {
                treeViewClickedNode = e.Node;
            }
        }

        private void DocumentActionToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
        {
            ToolStripMenuItem toolStripMenuItem = (ToolStripMenuItem)sender;

            if (toolStripMenuItem.CheckState == CheckState.Checked)
            {
                //Do something with treeViewClickedNode object
            }
        }
    }
}

Solution

  • The For a ContextMenuStrip portion of the answer for S.O. question Determine what control the ContextMenuStrip was used on almost answers my question.

    In my case, however, I want to process the ContextMenuStrip item right-click and access the ContextMenuStrip item's CheckState when the CheckState changes, so my code uses a ContextMenuStrip item _CheckStateChanged event method instead of a ContextMenuStrip item _Click event method. As such, I need to cast the sender to a ToolStripMenuItem instead of a ToolStripItem. Other than that, I use the For a ContextMenuStrip portion of the answer from the S.O. question Determine what control the ContextMenuStrip was used on in my DocumentActionToolStripMenuItem_CheckStateChanged event method :

    private void DocumentActionToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
    {
        Control treeNodeControl;
    
        ToolStripMenuItem toolStripMenuItem = (ToolStripMenuItem)sender;
    
        // if the ToolStripMenuItem item is owned by a ContextMenuStrip ...
        if (toolStripMenuItem.Owner is ContextMenuStrip contextMenuStrip)
        {
            // Get the TreeNode that is displaying this context menu
            treeNodeControl = contextMenuStrip.SourceControl;
    
            if (toolStripMenuItem.CheckState == CheckState.Checked)
            {
                //Do something with treeNodeControl.SelectedNode treeView node
            }
        }
    
    }