whizzzkey
whizzzkey

Reputation: 946

Caliburn.micro get treeview's selected item by contextmenu onclick

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

Answers (1)

Anu Viswan
Anu Viswan

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

enter image description here

For Node, enter image description here

Upvotes: 3

Related Questions