Reputation: 946
I have a task to use a contextmenu in treeview and pass selected treeview's item to ViewModel by clicking on contextmenu element.
Here is my xaml:
<Window.Resources>
<HierarchicalDataTemplate x:Key="Ufps"
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" />
<TextBlock Margin="5 0 0 0" Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
........
........
<TreeView x:Name="TrvUfpsDictionary" Height="222" Canvas.Left="25"
Canvas.Top="280" Width="545"
Background="AliceBlue"
ItemsSource="{Binding Path=Ufps, Mode=OneWay}"
ItemTemplate="{StaticResource Ufps}">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Element"
cal:Message.Attach="[Event Click] = [Action AddElement(TrvUfpsDictionary.SelectedItem)]"
/>
................
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
........
<Button Content="Test" Canvas.Left="475" Canvas.Top="568" Width="75"
cal:Message.Attach="[Event Click] = [Action AddElement(TrvUfpsDictionary.SelectedItem)]"/>
And here is simple ViewModel's code:
public class UserSettingsViewModel : PropertyChangedBase
{
..........
public void AddElement(object selectedItem)
{
MessageBox.Show("Element added! "+selectedItem.?GetHashCode());
}
..........
}
Now I've stuck with it. When I've selected treeview's item and then I've pressed the "Test" button - it works fine, it pass the selected item to "AddElement" in my VM. BUT when I do the same with contextmenu - it always pass null. Did I miss something?
EDIT I've made a simple app with the problem described. https://github.com/whizzzkey/WpfApp1
Upvotes: 1
Views: 719
Reputation: 18155
You might have to move the Context Menu further into the TreeView, into the Item Template and add Context Menu to the Label/TextBlock you have in nodes.
For example, consider the following Employee tree (emulating since I do not know your data structure),
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Positions}" >
<Label Content="{Binding DepartmentName}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Employees}" >
<Label Content="{Binding PositionName}"
Tag="{Binding DataContext, ElementName=TestControl}" >
<Label.ContextMenu>
<ContextMenu
cal:Action.TargetWithoutContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Element"
cal:Message.Attach="[Event Click] = [Action AddElement($datacontext)]"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Label Content="{Binding EmployeeName}"
Tag="{Binding DataContext, ElementName=TestControl}">
<Label.ContextMenu>
<ContextMenu
cal:Action.TargetWithoutContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Element"
cal:Message.Attach="[Event Click] = [Action AddElement($datacontext)]" />
</ContextMenu>
</Label.ContextMenu>
</Label>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
There are couple of important points to note here. Since your method exists in ViewModel and you have to ensure that the DataContext is pointing to your ViewModel instead of the Item Type that is bound to node.
For this, you need can make use of cal:Action.TargetWithoutContext
. The following line the Label definition ensure we have access to the View's DataContext.
Tag="{Binding DataContext, ElementName=TestControl}"
While the following line ensures the we get our bindings right (to ViewModel). TestControl
is the x:Name for your UserControl
cal:Action.TargetWithoutContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"
Finally the Click Action would be modified as following.
cal:Message.Attach="[Event Click] = [Action AddElement($datacontext)]"
This would ensure your ViewModel's Action is called with the right parameter passed.
Update
Based on your comment and code,following are the changes required.
Window Definition : Add x:Name
<Window
x:Class="WpfApp1.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
Title="XmlData Tree Test"
x:Name="TestControl"
Width="250"
Height="350">
Root Hierarchical Template Associating Item source with Tag is placed on the TextBlock, also the Relative Source has Self.
<HierarchicalDataTemplate DataType="root" ItemsSource="{Binding XPath=./*}" >
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0" Text="ROOT"
Tag="{Binding DataContext, ElementName=TestControl}">
<TextBlock.ContextMenu>
<ContextMenu
cal:Action.TargetWithoutContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Element"
cal:Message.Attach="[Event Click] = [Action AddElement($datacontext)]" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
Hierarchical Template for Node
<HierarchicalDataTemplate
DataType="Node"
ItemsSource="{Binding XPath=./*}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0" Text="Node:" />
<TextBlock Margin="5,0,0,0"
Tag="{Binding DataContext, ElementName=TestControl}"
Text="{Binding XPath=@name}" >
<TextBlock.ContextMenu>
<ContextMenu
cal:Action.TargetWithoutContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Element"
cal:Message.Attach="[Event Click] = [Action AddElement($datacontext)]" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
Output Example, For Root
Upvotes: 3