5

I'm dynamically generating a tree (TreeViewItems) and want to add the same context menu to each item in the tree. Because all of the context menus will be the same, I figured I could make one, and apply it to each TreeViewItem. (Maybe this is a bad idea?) Seems like this should work as long as the Click handler can figure out which TreeViewItem's context menu was opened.

I tried combining SO answers from here (getting the right-clicked object) and here (programmatically adding a binding) and came up with this:

ContextMenu carContextMenu;

public MainWindow()
{
    InitializeComponent();
    Initialize();
    ConstructTree();
}

void ConstructTree()
{
    string[] carNames = {"Mustang", "Viper", "Jetta"};

    foreach (string car in carNames)
    {
        TreeViewItem carNode = new TreeViewItem();
        carNode.Header = car;
        carNode.ContextMenu = carContextMenu;

        CarTree.Items.Add(carNode);
    }
}

void Initialize()
{
    carContextMenu= new ContextMenu();
    MenuItem newQuery = new MenuItem();
    newQuery.Header = "Drive car...";

    Binding b = new Binding("Parent");
    b.RelativeSource = RelativeSource.Self;

    newQuery.SetBinding(MenuItem.CommandParameterProperty, b);
    newQuery.Click += NewQuery_Click;

    carContextMenu.Items.Add(newQuery);
}

void NewQuery_Click(object sender, RoutedEventArgs e)
{
    MenuItem mi = sender as MenuItem;
    if (mi != null)
    {
        ContextMenu cm = mi.CommandParameter as ContextMenu; // *****
        if (cm != null)
        {
            TreeViewItem node = cm.PlacementTarget as TreeViewItem;
            if (node != null)
            {
                Console.WriteLine(node.Header); // car name, ideally
            }
        }
    }
}

At runtime, when it gets to the line with asterisks, mi.CommandParameter is null, so it skips the rest of the method. What's going on with my approach? Honestly, I'm a little surprised that the right-clicked item isn't an intrinsic part of the event handler arguments, given how often you'd want to know what was clicked. Tree items aren't necessary selected when they're right-clicked, so checking that isn't a reliable method... plus it'd just be a hacky workaround.

Thanks!

Community
  • 1
  • 1
Benjin
  • 2,264
  • 2
  • 25
  • 50
  • are you familiar with how to use the `Sender` to determine and or get the name of the object that was clicked on ..? – MethodMan Oct 26 '16 at 17:51
  • I guess not? I walked through the `MenuItem` (`sender`'s type) properties and didn't spot anything useful. Same with the RoutedEventArgs. Maybe the fact that I'm attaching the same `ContextMenu` object to multiple `TreeViewItems` is messing something up? – Benjin Oct 26 '16 at 18:08
  • you can check for the senders ID which would be the name I do this to determine what menu item at runtime currently that the user clicks on by wiring all the menu Items to the same event and then using a switch / case to determine which menu item was clicked – MethodMan Oct 26 '16 at 18:13
  • I will post an example of how I am doing it and every ToolStripMenItem event I point to the same one – MethodMan Oct 26 '16 at 18:14
  • what does your XAML look like do you have something like this `CommandParameter="{Binding RelativeSource={RelativeSource Self}}">` – MethodMan Oct 26 '16 at 18:45
  • or try something like this `CommandParameter="{Binding Path=PlacementTarget.SelectedItems, RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}} }"` – MethodMan Oct 26 '16 at 18:46

1 Answers1

3

Naturally, it turns out I was overcomplicating things and the links I was following were either incorrect, out-of-date, or (most likely) I misread some portion of their scenario and there was something that didn't actually apply to me.

I didn't need any Binding on the MenuItem itself and simply should have been looking at myMenuItem.Parent.PlacementTarget the whole time. Working code below:

void Initialize()
{
    carContextMenu= new ContextMenu();
    MenuItem newQuery = new MenuItem();
    newQuery.Header = "Drive car...";

    newQuery.Click += NewQuery_Click;

    carContextMenu.Items.Add(newQuery);
}

void NewQuery_Click(object sender, RoutedEventArgs e)
{
    MenuItem mi = sender as MenuItem;
    if (mi != null)
    {
        ContextMenu cm = mi.Parent as ContextMenu;
        if (cm != null)
        {
            TreeViewItem node = cm.PlacementTarget as TreeViewItem;
            if (node != null)
            {
                Console.WriteLine(node.Header);
            }
        }
    }
}  
Benjin
  • 2,264
  • 2
  • 25
  • 50