WPF树形菜单上下文菜单命令参数

3

我有一个使用HierarchicalDataTemplateTreeView。在TreeView上我有一个ContextMenu

该内容涉及IT技术,请您知悉并进行相应翻译。
<TreeView Name="_packageTreeView" ItemsSource="{Binding PackageExtendedList}" Behaviors:TreeViewInPlaceEditBehavior.IsEditable="True">
    <TreeView.ContextMenu>
        <ContextMenu StaysOpen="true">
            <MenuItem Header="Добавить пакет" Height="20" Command="{Binding AddPackageCommand}" 
                CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}">
                    <MenuItem.Icon>
                        <Image Source="/Resources/ManualAdd.png" Width="15" Height="15"></Image>
                    </MenuItem.Icon>
            </MenuItem>
        </ContextMenu>
   </TreeView.ContextMenu>
   <TreeView.ItemTemplate>
       <HierarchicalDataTemplate ItemsSource="{Binding Childs}">bla bla bla</HierarchicalDataTemplate>
   </TreeView.ItemTemplate>
</TreeView>

正如您所看到的,我将Command绑定到菜单项。AddPackageCommand通常在ViewModell类中定义。调用命令可以正常工作,但是我总是在CommandParameter中得到null。我发现一些问题与我的类似,但我不明白解决方法。例如:WPF中ContextMenu中的CommandParameters。无论如何,这对我没有用 :( 我做错了什么?更新:那似乎可以工作,但是所有内容都是相同的,我不明白为什么CommandParameter不能与TreeView.Name一起使用。
CommandParameter="{Binding PlacementTarget, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"

例如,这样的示例可以正常工作。
<i:EventTrigger EventName="SelectedItemChanged">
    <i:InvokeCommandAction Command="{Binding PackageTreeItemChangeCommand}" CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}"/>
</i:EventTrigger>

究竟是怎么回事...

而且,我在CommandParameter中有一个TreeView对象,而不是TreeViewItem。我可以从TreeView中获取SelectedItem,但是如何将确切的TreeViewItem作为CommandParameter发送呢?

致Sheridan:

问题是为什么这样做不起作用。

CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}"

并且这个有效

CommandParameter="{Binding PlacementTarget, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"

为什么有时我可以直接使用TreeView控件的名称,而有时却不能?
据我所知,问题在于TreeView控件和ContextMenu具有不同的DataContext,因为ContextMenu具有自己的VisualTree,它并不是TreeView ViaualTree的一部分。
不幸的是,该方法也无法奏效,我又一次遇到了null。 我确实设置了TreeView.Tag。
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={
            RelativeSource Self}}" StaysOpen="true">
    <MenuItem Header="Добавить пакет" Height="20" Command="{Binding AddPackageCommand}" 
                CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}">
        <MenuItem.Icon>
             <Image Source="/Resources/ManualAdd.png" Width="15" Height="15"></Image>
        </MenuItem.Icon>
    </MenuItem>
</ContextMenu>

这是最简单的方法,但如果我在ViewModel中有SelectedItem属性,则将其绑定到CommandParameter没有意义,因为我已经在ViewModel中拥有了它。

 <MenuItem Header="Добавить пакет" Height="20" Command="{Binding AddPackageCommand}" 
    CommandParameter="{Binding SelectedItem}">
    <MenuItem.Icon>
        <Image Source="/Resources/ManualAdd.png" Width="15" Height="15"></Image>
    </MenuItem.Icon>
</MenuItem>

1
这些东西太不必要地复杂了。 - hinst
1个回答

2
你向我们展示了你已经有了答案,为什么你还要发布另一个关于同一主题的问题,而不是简单地遵循答案中的示例呢?这对你不起作用,因为你没有正确复制答案。
在你的示例帖子答案中,Tag属性设置为菜单应用的TreeView控件,但你没有这样做。
你下一个问题是你再次忽略了这个Tag属性在CommandParameter中... 你不知何故将其从正确的答案改变了:
CommandParameter="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource 
    FindAncestor, AncestorType={x:Type ContextMenu}}}

在你的问题中是指这个:
CommandParameter="{Binding PlacementTarget, RelativeSource={RelativeSource 
    FindAncestor, AncestorType={x:Type ContextMenu}}}"

你所需要做的就是复制粘贴它。不过,你可能会更幸运,如果你这样做:
<TreeView Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}" 
    Name="_packageTreeView" ItemsSource="{Binding PackageExtendedList}" 
    Behaviors:TreeViewInPlaceEditBehavior.IsEditable="True">
    <TreeView.ContextMenu>
        <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={
            RelativeSource Self}}" StaysOpen="true">
            <MenuItem Header="Добавить пакет" Height="20" Command="{Binding AddPackageCommand}" 
                CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}">
                    <MenuItem.Icon>
                        <Image Source="/Resources/ManualAdd.png" Width="15" Height="15"></Image>
                    </MenuItem.Icon>
            </MenuItem>
        </ContextMenu>
   </TreeView.ContextMenu>
   <TreeView.ItemTemplate>
       <HierarchicalDataTemplate ItemsSource="{Binding Childs}">bla bla bla</HierarchicalDataTemplate>
   </TreeView.ItemTemplate>
</TreeView>

看一下 TreeView.Tag 属性... 这个属性被设置为它自己的 DataContext,这意味着无论是什么被设置为 TreeViewDataContext,都可以在 Tag 属性对象中使用。
接下来,看一下 ContextMenu.DataContext 属性... 这个属性被设置为 PlacementTargetTag 属性,而 PlacementTarget 是应用到 ContextMenu 的控件,或者在这种情况下,是 Treeview
如果你还没有弄清楚,这意味着 ContextMenuDataContext 现在被设置为与 TreeViewDataContext 相同的对象。如果这不是你想要的,因为你的 Command 在另一个对象上,那么只需更改 Tag 属性中的 Binding 路径,指向具有 Command 的对象所在的位置即可。
你可以做的最后一件事是在你的视图模型/代码之后添加一个绑定到TreeView.SelectedItem属性的属性,以使它更简单。
<TreeView SelectedItem="{Binding SelectedItem}"... />

然后,你可以简单地将这个属性用作你的CommandParameter参考:
<MenuItem Header="Добавить пакет" Height="20" Command="{Binding AddPackageCommand}" 
    CommandParameter="{Binding SelectedItem}">
    <MenuItem.Icon>
        <Image Source="/Resources/ManualAdd.png" Width="15" Height="15"></Image>
    </MenuItem.Icon>
</MenuItem>

这个最后一部分当然是假设您已经将视图模型/代码设置为TreeViewTag属性。如果您仍然不理解,请查看WPF Tutorial.NET上的WPF中的上下文菜单页面。
更新 >>>
我简直不明白你为什么要发布这个问题。首先,你说你不能做某事,但是后来向我们提供了另一个帖子中的有效解决方案的链接。在试图帮助您之后,您说它确实起作用了,但您不知道为什么......但是您再次正确地回答了自己的问题:

据我所知,TreeView控件和ContextMenu的DataContext不同,因为ContextMenu具有自己的VisualTree,并且不是TreeView ViaualTree的一部分。

正如你所说,ContextMenu 有它自己的可视树。这意味着它不知道另一个可视树中的控件,无论它们是否被命名。然而,如果为 ContextMenu.DataContext 提供了一个对象,例如包含视图,则它可以知道另一个可视树中的控件(更具体地说,是视图中控件的可视树)。
整个问题似乎在于你对于 Binding 的一般知识和 Binding.Path 语法的缺乏了解。请参考以下 MSDN 文章获取更多有关此主题的帮助:

Binding.Path 属性

属性路径语法

RelativeSource MarkupExtension

太多人试图在学会基础之前就使用 WPF。

在试图帮助你之后,你说它起作用了,但你不知道为什么。不,我说的是你的方法不起作用(我复制了你的代码)。使用将DataContext绑定到TreeView.Tag等的方法。这是我在发布的链接中建议的方法。即使我将ContextMenu.DataContext属性设置为TreeView.Tag,我仍然在CommandParameter中得到null :(无论如何,谢谢。 - monstr
因为 无法使我的示例工作并不意味着它不起作用。这只是意味着你不理解它。如果你花时间学习我建议的 Binding 语法,那么也许你会理解它。 - Sheridan
1
我说过你很蠢吗?没有,我只是说你不理解,这显然是真的。你难道看不到我努力帮助你和写了多少吗?我甚至不想回复你最后的评论,因为它们真的很愚蠢。 - Sheridan

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