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
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
For Node,
Related
I have a problem with Menu Item Command Binding. I have used MVVM pattern
When I use right click, the menu appears. But when I click on the menu item doesn't work. Do you know why? Thanks
Here is the XAML:
<UserControl x:Class="PlotView.ViewModel.PlotViewControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
d:DesignHeight="400" d:DesignWidth="600"
x:Name="theViewName">
<UserControl.Resources>
</UserControl.Resources>
<GroupBox Name="GB" Header="Graphs" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="0">
<ListView Name="PlotLista" SelectedIndex="{Binding SelectedValue}" ItemsSource="{Binding PlotModelList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate>
<oxy:Plot MinHeight="260" Height="Auto" IsRendering="True" FontStyle="Normal" FontFamily="Arial" FontSize="8" VerticalContentAlignment="Top" HorizontalContentAlignment="Left" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=ActualWidth}" Model="{Binding }">
<oxy:Plot.ContextMenu>
<ContextMenu>
<MenuItem Header="Export to PNG" Command="{Binding DataContext.SavePNG, ElementName=theViewName}"/>
</ContextMenu>
</oxy:Plot.ContextMenu>
</oxy:Plot>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GroupBox>
</UserControl>
Here are a small portion of ViewModel:
#region Fields
private readonly DelegateCommand _menuClick=new DelegateCommand(this.MenuItemClick);
#endregion
#region Command
public ICommand SavePNG
{
get { return this._menuClick; }
}
#endregion
private void MenuItemClick()
{
// some code here
}
Error:
System.Windows.Data Error: 40 : BindingExpression path error:
'SavePNG' property not found on 'object' ''PlotModel'
(HashCode=15385318)'. BindingExpression:Path=SavePNG;
DataItem='PlotModel' (HashCode=15385318); target element is 'MenuItem'
(Name=''); target property is 'Command' (type 'ICommand')
Your binding is trying to find SavePNG on your Item, not your ViewModel.
Instead, give your view an x:Name, or a Name, and use the following binding instead:
{Binding DataContext.SavePNG, ElementName=theViewName}
Assuming that SaveCommand is on the same ViewModel which contains your collection.
ContextMenus are a little different in wpf as they are not part of the visual tree of the control. As such they cannot 'see' any elements by relative source or by elementnames.
A little cheat is to use the PlacementTarget property and get the datacontext with that. Change your ListView to below to make it work. Notice the tag property on the ListView and the DataContext property on the ContextMenu.
<ListView Name="PlotLista" SelectedIndex="{Binding SelectedValue}" ItemsSource="{Binding PlotModelList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Tag="{Binding DataContext,ElementName=theViewName}">
<ListView.ItemTemplate>
<DataTemplate>
<oxy:Plot MinHeight="260" Height="Auto" IsRendering="True" FontStyle="Normal" FontFamily="Arial" FontSize="8" VerticalContentAlignment="Top" HorizontalContentAlignment="Left" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=ActualWidth}" Model="{Binding }">
<oxy:Plot.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Export to PNG" Command="{Binding SavePNG}"/>
</ContextMenu>
</oxy:Plot.ContextMenu>
</oxy:Plot>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This wasn't written in visual studio so please check the syntax:
<ListView Tag="{Binding Path=DataContext,ElementName=theViewName}">
<ListView.ItemTemplate>
<DataTemplate>
<oxy:Plot Tag="{Binding Path=Tag,RelativeSource={RelativeSource AncestorType=ListView}">
<oxy:Plot.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Command="{Binding SavePNG}"/>
</ContextMenu>
</oxy:Plot.ContextMenu>
</oxy:Plot>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have a treeview using the following style:
<HierarchicalDataTemplate x:Key="itemTemplate" DataType="{x:Type AttCatalog:AttachmentCatalogModel}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" Tag="{Binding Guid}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="New Item"/>
<MenuItem Header="Move to..." />
<MenuItem Header="Delete" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
<TreeView x:Name="tree" HorizontalAlignment="Left" Width="216" BorderThickness="0,0,1,0" Background="#FFFBFBFB" IsEnabled="{Binding IsEnabled}" ItemsSource="{Binding Catalogs}" ItemTemplate="{StaticResource itemTemplate}" TreeViewItem.Expanded="OnExpandItemHandler" Margin="0,0,0,241" SelectedItemChanged="tree_SelectedItemChanged">
you can see TextBlock Tag binding a Guid property, my question is, how to do that when this Guid is empty (00000-00000000-00000), not show the contextMenu?
In your context menu, bind the visibility to the Guid, and treat it with a converter =>
<ContextMenu Visibility="{Binding Element=Guid,
Converter={StaticResource GuidToVisibilityConverter}}">
<MenuItem Header="New Item"/>
<MenuItem Header="Move to..." />
<MenuItem Header="Delete" />
</ContextMenu>
In your converter, you can treat your Guid value the way you want, to either return Visibility.Visible or Visibility.Hidden, depending on the value.
You can find more information on converters here.
Hope that helped !
I want to bind data to radtreeview through Hireachical Data Template. With Wpf treeview Its working. We wanted to change that to Radtreeview. When i am trying to achieve the same i am not getting that.
<TreeView Name="treeview2"
Grid.RowSpan="2" Tag=""
Grid.ColumnSpan="2"
ItemContainerStyle="{StaticResource StretchTreeViewItemStyle}" ItemsSource="{Binding Path=Cluster}" Width="250" Height="251" Margin="0,0,0,0" DockPanel.Dock="Bottom">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<DockPanel LastChildFill="True">
<TextBlock Padding="15,0,30,0" Text="{Binding Path=numitems}" TextAlignment="Right" DockPanel.Dock="Right"/>
<TextBlock Text="{Binding Path=Text}" DockPanel.Dock="Left" TextAlignment="Left">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" />
<MenuItem Header="Exlcude"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DockPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
I am looking for a sample which shows that bind a property. Cluster is a observablecollection property of type a class. Nodes is a child collection with in that. I am looking for transforming the same code to radtreeview-- Thanks
What I want
change the properties(e.g. background) of a context menu in a datatemplate in the runtime from the code behind.
What is the XAML
<DataTemplate x:Key="ListsDataTemplate">
<StackPanel x:Name="stackPanel" Margin="0,0,0,10">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu x:Name="myMenu" IsFadeEnabled="False" IsZoomEnabled="False">
<toolkit:MenuItem x:Name="edit" Header="{Binding LocalStrings.bt_menu_Edit, Source={StaticResource LocalizedResources}}" Click="menuItemEdit_Click" />
<toolkit:MenuItem x:Name="postpone" Header="{Binding LocalStrings.bt_menu_Postpone, Source={StaticResource LocalizedResources}}" Click="menuItemPostpone_Click" />
<toolkit:MenuItem x:Name="email" Header="{Binding LocalStrings.bt_menu_Email, Source={StaticResource LocalizedResources}}" Click="menuItemEmail_Click" />
<toolkit:MenuItem x:Name="sms" Header="{Binding LocalStrings.bt_menu_Sms, Source={StaticResource LocalizedResources}}" Click="menuItemSMS_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<ListBox
HorizontalAlignment="Left"
DataContext="{Binding}"
ItemTemplate="{StaticResource ListsDataTemplate}"
VerticalAlignment="Top"
Width="432"
Margin="0,81,0,0"
x:Name="myListBox">
</ListBox>
Add a Loaded="myMenu_Loaded" handler to myMenu, and in that handler declare var myMenu = (ContextMenu)sender;.
Since myMenu is in a DataTemplate and is a template for creating objects rather than the instance that will actually be used on the page, no field is auto-generated for it.
How I can set selected item or get listbox item in which I have ContextMenu?
<local:TypeTemplateSelector Content="{Binding}">
<local:TypeTemplateSelector.WithAudio>
<DataTemplate>
<Grid Margin="0,5">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Open="needselect" x:Name="databoundMenu">
<toolkit:MenuItem Header="I Like It"/>
<toolkit:MenuItem>
<toolkit:MenuItem.Header>
<CheckBox Content="Tell" ></CheckBox>
</toolkit:MenuItem.Header>
</toolkit:MenuItem>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<StackPanel>
<TextBlock Text="{Binding h}"/>
</StackPanel>
</Grid>
</DataTemplate>
</local:TypeTemplateSelector.WithAudio>
I'm not sure if I understand your question: you have a listbox in which each item has a contextmenu and in the context menu you want to know the selected item ? Here's my usage of the contextmenu
<DataTemplate>
<Border>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Move" Command="{Binding MoveCurrentCommand}" CommandParameter="{Binding}" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Grid Margin="0,0,0,15" Height="100">
(...)