WPF treeview contextmenu command parameter

让人想犯罪 __ 提交于 2019-12-12 13:24:58

问题


I have TreeView with HierarchicalDataTemplate. On TreeView I have ContextMenu

<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>

As you can see, I bind Command to menu item. AddPackageCommand defined in ViewModell class as usually. Invoke command works fine, but I always have null in CommandParameter. I found some questions similar to my, but I don't understand solutions. For example:

CommandParameters in ContextMenu in WPF

Anyway it doesn't work for me :( What am I doing wrong?

Updated

That seems to be working, but it's all the same, I don't understand why CommandParameter doesn't work with TreeView.Name.

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

for examplle, such a sample works fine

<i:EventTrigger EventName="SelectedItemChanged">
    <i:InvokeCommandAction Command="{Binding PackageTreeItemChangeCommand}" CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}"/>
</i:EventTrigger>

What's the hell...

And anyway, I have TreeView object in CommandParameter, not TreeViewItem. I can get SelectedItem from TreeView, but how can I send exactly TreeViewItem as CommandParameter?

to Sheridan

Question was WHY this doesn't work.

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

And this works

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

WHY sometimes I can use direct TreeView control name and sometimes I cannot. As I understand, matter is different DataContext of TreeView control and ContextMenu because ContextMenu has its own VisualTree and it is not the part of TreeView ViaualTree.

Unfortunately, that approach doesn't work too, I have null again. I set TreeView.Tag, sure.

<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>

This is the easiest way, but if I have SelectedItem property in ViewModel it has no sense bind it to CommandParameter, because I already have it in 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:


You showed us that you already have an answer... why on earth did you post yet another question on this same subject instead of simply following the example in the answer? It doesn't work for you, because you didn't copy the answer properly.

In your example post answer, the Tag property is set to the TreeView control that the menu is applied on, but you haven't done this.

Your next problem is that you have ignored this Tag property again in the CommandParameter... somehow, you have changed this from the correct answer:

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

to this in your question:

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

All you needed to do was copy and paste it. All the same, you might have even more luck doing this:

<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>

Look at the TreeView.Tag property... this is set to its own DataContext, which means that whatever is set as the DataContext of the TreeView is now available in the Tag property object.

Next, look at the ContextMenu.DataContext property... this is set to the Tag property of the PlacementTarget, which is the control that the ContextMenu is applied to, or in this case, the Treeview.

If you haven't worked it out yet, this means that the DataContext of the ContextMenu is now set to the same object as the DataContext of the TreeView. If this is not what you want because your Command is on a different object, then just change the Binding path in the Tag property to point to wherever the object that had the Command is.

The last thing that you can do to make this simpler is to add a property to your view model/code behind that binds to the TreeView.SelectedItem property:

<TreeView SelectedItem="{Binding SelectedItem}"... />

Then you can simply refer to this property for your 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>

This last part of course assumes that you have set your view model/code behind as the Tag property of the TreeView. If you still don't understand this, take a look at the Context Menus in WPF page on WPF Tutorial.NET.

UPDATE >>>

I simply don't understand why you posted this question. First you said you couldn't do something, but then provided us with a link to a valid solution in another post. After trying to help you, you then say that it did work, but you don't know why... but then you correctly answered your own question again:

As I understand, matter is different DataContext of TreeView control and ContextMenu because ContextMenu has its own VisualTree and it is not the part of TreeView ViaualTree.

As you said, the ContextMenu has its own visual tree. This means that it is not aware of controls, named or otherwise, in another visual tree. However, if the ContextMenu.DataContext is provided with an object such as the containing view, then it can be aware of controls in another visual tree (more specifically, the visual tree of the controls in the view).

This whole issue seems to be down to a lack of knowledge on your part about Binding in general and Binding.Path syntax more specifically. Please take a look at the following articles on MSDN for more help on this topic:

Binding.Path Property

Property Path Syntax

RelativeSource MarkupExtension

So many people try to run with WPF before they can walk.



来源:https://stackoverflow.com/questions/19133654/wpf-treeview-contextmenu-command-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!