以编程方式添加一个上下文菜单,其单击处理程序知道右键单击的是哪个项目

5
我正在动态生成一个树形结构(TreeViewItem),并想给树中的每个项目添加相同的上下文菜单。由于所有上下文菜单都相同,我认为我可以创建一个菜单,并将其应用于每个TreeViewItem。(也许这是个坏主意?)只要Click处理程序能够找出已打开的哪个TreeViewItem的上下文菜单,似乎应该能行得通。
我尝试结合此处(获取右键单击的对象)和此处(以编程方式添加绑定)的SO答案,并得出以下结论:
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
            }
        }
    }
}

在运行时,当到达带有星号的那一行时,mi.CommandParameter 为空,因此跳过了方法的其余部分。我的方法出了什么问题?说实话,我有点惊讶右键单击的项目不是事件处理程序参数的固有部分,考虑到你经常想知道点击了什么。右键单击树形项时并不一定会选择它们,因此检查它们并不是一种可靠的方法...而且这只是一个 hacky 的解决方法。
谢谢!

你是否熟悉如何使用“Sender”来确定或获取被点击对象的名称? - MethodMan
我猜不是这个原因?我遍历了“MenuItem”(“sender”的类型)属性,但没有发现任何有用的信息。RoutedEventArgs也一样。也许是因为我将相同的“ContextMenu”对象附加到多个“TreeViewItems”上导致了某些问题? - Benjin
您可以检查发送者的ID,这将是我用来确定用户在运行时单击了哪个菜单项的名称,通过将所有菜单项连接到同一事件,然后使用switch / case来确定单击了哪个菜单项。 - MethodMan
我将发布一个示例,展示我是如何做到的,每个ToolStripMenItem事件都指向同一个。 - MethodMan
你的XAML长什么样子?你有像这样的东西吗? CommandParameter="{Binding RelativeSource={RelativeSource Self}}"> - MethodMan
CommandParameter="{Binding Path=PlacementTarget.SelectedItems, RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}} }" - MethodMan
1个回答

3

自然而然的,事实证明我一直在过于复杂化事情,我所跟随的链接要么是不正确的、过时的,要么(最有可能的)是我错读了某部分场景,有些东西实际上并不适用于我。

我不需要在MenuItem本身上进行任何绑定,整个时间我只需要查看myMenuItem.Parent.PlacementTarget即可。下面是可行的代码:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接