How to get a treeview item node value using a ContextMenu using WPF and C#

The first things you need to note is that the treeview node needs to be selected in order for you to get any information from it. I did read some articles about using the System.Windows.Point to get the X, Y coordinates of the mouse, but I chose to go another route. Using the Point class just seemed too distant from what I was trying to achieve. I mean, it seemed like I was referencing a third party library. I like to keep my programming inline and as direct/mainstream/pure as possible. I.e. use the capabilities of the class you are using instead of instantiating another one, if you can.

I was able to find some examples on how to set focus on the treeview node when it is right clicked.

Here is an example of how my Treeview looks like within the WPF my WPF Grid.

<treeview x:name="TV1" previewmouserightbuttondown="TV1_PreviewMouseRightButtonDown">
  <treeview.contextmenu>
    <contextmenu>
     <menuitem name="CMI1" header="MI1" click="TV1_CMI1_Click">
     <menuitem name="CMI2" header="MI2" click="TV1_CMI2_Click">
    </menuitem></menuitem></contextmenu>
  </treeview.contextmenu>
</treeview>

In my code behind, I needed to write 3 methods. The first method is passed a DependencyObject class, which is our case will be the OriginalSource object of the MouseButtonEventArgs class. The method will use the GetParent method of the VisualTreeHelper to loop up the object until we find a TreeViewItem. Once found, the method returns it.

private static DependencyObject
SearchTreeView<t>(DependencyObject source)
{
  while (source != null &amp;&amp; source.GetType() != typeof(T))
  {
    source = VisualTreeHelper.GetParent(source);
  }
  return source;
}

The second method is triggered from the right mouse click within the TreeView (TV1). This method calls the SearchTreeView method and then marks the right clicked TreeViewItem as selected.

private void TV1_PreviewMouseRightButtonDown(object sender,
  MouseButtonEventArgs e)
{
  TreeViewItem treeViewItem =
    (TreeViewItem)SearchTreeView<treeviewitem>
    ((DependencyObject)e.OriginalSource);
  if (treeViewItem != null)
  {
    treeViewItem.IsSelected = true;
    e.Handled = true;
  }
}

Here is where it gets a little bit tricky. In the third method is where the code is to get the selected value from the ContextMenu, the selected TreeViewItem and perform your program specific actions. The trick is to cast or box the TV1.SelectedValue object into what you added to the TreeView when you created it.

I built a TreeViewSelectionModel class when I built TV1. My class contained properties like Name, ID, etc… and has a List of Children nodes too. Adding a parent Node to the tree looks something like this:

List<treeviewselectionmodel> treeView =
  new List<treeviewselectionmodel>();
TreeViewSelectionModel tree =
  new TreeViewSelectionModel("TopLevelTreeNode");
tree.ID = 12345;
treeView.Add(tree);

And then when I want to add a child node to the parent, the code would look something like this:

TreeViewSelectionModel child =
  new TreeViewSelectionModel("ChildItem1");
child.ID = 23456;
tree.Children.Add(child);

That being said, my third method, I capture the OriginalSource object which I can use to tell me which MenuItem was selected from the ContextMenu. Then I cast the TV1.SelectedValue object to the class which was used when building the TreeView. After doing this cast, I am able to access the properties, fields and lists which exist within the class.

private void TV1_CMI1_Click(object sender, RoutedEventArgs e)
{
  MenuItem item = (MenuItem)e.OriginalSource;
  TreeViewSelectionModel selected =
    (TreeViewSelectionModel)TV1.SelectedValue;
  if (item.Name.Equals("CMI1"))
  {
    int ID = selected.ID;
    //... program specific logic
  }
}

The most challenging part of this was to find out how to get the access to the values in the SelectedValue object. Once I viewed the contents of it in Debug mode, I immediately recognized it as my TreeViewSelectionModel class and the lightbuld came on after that.