I have a ContextMenu binded to a CommandBinding:
<MetroTreeView:MetroTreeView x:Name="tvTagList" Grid.Row="1" Margin="15, 0, 15, 0" SelectedItemChanged="tvTagList_SelectedTagChanged" ItemsSource="{Binding}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{x:Null}" BorderBrush="{DynamicResource ExtryzeAccentBrushSecondary}" BorderThickness="2" Foreground="White"
VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Standard">
<MetroTreeView:MetroTreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagClass}" ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock>
<Run Text="{Binding TagClassMagic}" />
<Run Foreground="{StaticResource TextBrushSecondary}" Text=" - " />
<Run Foreground="{StaticResource TextBrushSecondary}" Text="{Binding Path=Description, Converter={StaticResource IValueTagDescriptionCleanup}}" />
</TextBlock>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Dump Tag List..." Click="DumpClassTagList" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type DataBind:TagEntry}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<StackPanel.ContextMenu>
<ContextMenu>
<ContextMenu.CommandBindings>
<CommandBinding Command="{x:Static DataBind:HaloMap.CommandTagBookmarking }"
CanExecute="CommandTagBookmarking_CanExecute"
Executed="CommandTagBookmarking_Executed"
/>
</ContextMenu.CommandBindings>
<MenuItem Header="Bookmark Tag" Foreground="White" Command="{x:Static DataBind:HaloMap.CommandTagBookmarking}" CommandParameter="{Binding}" />
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding TagFileName}" />
</StackPanel>
</DataTemplate>
</MetroTreeView:MetroTreeView.Resources>
<MetroTreeView:MetroTreeView.ItemContainerStyle>
<Style TargetType="{x:Type MetroTreeView:MetroTreeViewItem}" BasedOn="{StaticResource {x:Type MetroTreeView:MetroTreeViewItem}}">
<EventSetter Event="MouseDoubleClick" Handler="tvTagList_ItemDoubleClick" />
</Style>
</MetroTreeView:MetroTreeView.ItemContainerStyle>
But when I run the CanExecute command, every attempt to access the DataContext is always met with a null:
private void CommandTagBookmarking_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
var a = e.Source;
var b = ((MenuItem)a).DataContext;
var c = ((ContextMenu) ((MenuItem) e.Source).Parent).DataContext;
}
Try this:
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
Since ContextMenu don't lie in the VisualTree of the object where it's placed. You have to inherit the DataContext of object using PlacementTarget -
<ContextMenu DataContext="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
Related
I have a treeview with hierarchical data template and I am trying to set DataContext for ContextMenu, so I could bind commands to it. I have done research and know that ContextMenu doesn't inherit DataContext of its parent. I tried to follow these posts: How to set the RelativeSource in a DataTemplate that is nested in a HierarchicalDataTemplate?
How to bind to the DataContext of a HierarchicalDataTemplate from its ItemTemplate XAML? but still can't get it to work. Any help would be appreciated. Here is my sample code:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewModels:SiteViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
</StackPanel.Resources>
<Image Width="16" Height="16" Margin="3,0" />
<TextBlock Text="{Binding SiteName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type viewModels:LevelViewModel}" ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal" >
<Image Width="16" Height="16" Margin="3,0" />
<TextBlock Text="{Binding LevelName}" >
<TextBlock.ContextMenu >
<ContextMenu>
<MenuItem Header="Test" Command="{Binding ?????????" CommandParameter="{Binding}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
One way to solve it:
In my case I had something like that:
<DataTemplate DataType="...">
<TreeView>
<TreeViewItem Tag="{Binding ElementName=LocalControl, Path=DataContext}"
Header="{Binding ...}"
ContextMenu="{StaticResource ...}">
...
</TreeViewItem>
</TreeView>
</DataTemplate>
You need to bind Tag attribute of parent TreeViewItem to its DataContext, then somewhere in your hierarchical templates for context menu you should bind its DataContext to Tag of parent control:
<ContextMenu x:Key="CyclogramFolderContextMenu"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<TextBlock Text="Do something" >
<TextBlock.InputBindings>
<MouseBinding Command="{Binding Path=(viewModels:someViewModel.SomeCommand)}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
</ContextMenu>
I created a tabcontrol with TabItem dynamic, and each TabItem with a button to close it, but just want that button visible when the TabItem is selected.
But I can not access the control inside the DataTemplate
<TabControl Name="dynamicTab" ItemsSource="{Binding}" Margin="0,85,0,0">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<Button
Focusable="False"
BorderThickness="0"
Background="Transparent"
BorderBrush="Transparent"
Padding="-4"
Height="10"
Width="10"
Name="btnDelete" Visibility="Hidden" DockPanel.Dock="Right" Margin="5,0,0,0" Click="btnDelete_Click"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Name="imgButtonClose" Source="/Recursos;component/Imagens/close16x16.png" Height="10" Width="10"/>
</Button>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
</DockPanel>
</DataTemplate>
</TabControl.Resources>
</TabControl>
Just use the binding on the IsSelected property of ancestoral TabItem:
<BooleanToVisibilityConverter x:Key="boolToVisibilityConverter"/>
...
<Button ...
Name="btnDelete"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=IsSelected, Converter={StaticResource boolToVisibilityConverter}">
...
</Button>
If you have no problems with this binding:
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}"
then the proposed code should work.
How to display context menu for tree view item in wpf using the hierarchical data template? How to display context menu only for CountryTemplate:
<HierarchicalDataTemplate x:Key="DispTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}" Tag="{Binding Path=Tag}">
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="BuildingTemplate" ItemsSource="{Binding Path=Building}" ItemTemplate="{StaticResource BuildingTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="CityTemplate" ItemsSource="{Binding Path=City}" ItemTemplate="{StaticResource CityTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=RootName}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
You also can add the ContextMenu to any visual child in the data template, for instance:
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Header" Command="{Binding Command}"/> <!--This command should be in the data context (each country item)-->
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Path=RootName}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}"/>
</StackPanel>
</HierarchicalDataTemplate>
One of the reasons why context menus do not work as cleanly as they could is because by default, they are in a different visual tree to everything else, so the DataContext cannot be found.
The key insight is to create a <Style> that defines a context menu,
then attach that style to a target element, which hooks up the context
menu. This shifts the context menu into a visual tree that is lined up with the default DataContext you want.
First, create the style:
<UserControl.Resources>
<ResourceDictionary>
<!-- For the context menu to work, we must shift it into a style, which means that the context menu is now in a
visual tree that is more closely related to the current data context. All we have to do then is set the style,
which hooks up the context menu. -->
<Style x:Key="ContextMenuStyle" TargetType="{x:Type StackPanel}">
<Setter Property="ContextMenu" Value="{DynamicResource TreeViewContextMenu}"/>
</Style>
<ContextMenu x:Key="TreeViewContextMenu">
<MenuItem Header="Test" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CmdDisplayDetailsGraph}"/>
</ContextMenu>
Then, hook the context menu up anywhere you want, without running into issues caused by different visual trees.
Example 1:
<HierarchicalDataTemplate DataType="{x:Type snapshot:Details}" ItemsSource="{Binding DetailsList}">
<StackPanel Orientation="Vertical" Style="{StaticResource ContextMenuStyle}">
<ContentPresenter Content="{Binding}" ContentTemplate="{Binding View.DefaultDataRowTemplate}" />
</StackPanel>
Example 2:
<DataTemplate DataType="{x:Type snapshot:InstrumentDetails}">
<StackPanel Orientation="Vertical" Style="{StaticResource ContextMenuStyle}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<HierarchicalDataTemplate x:Key="CountryTemplate" ItemsSource="{Binding Path=Country}" ItemContainerStyle="{StaticResource CountryTemplateItemContainerStyle}" ItemTemplate="{StaticResource CountryTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=RootName}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}" />
</StackPanel>
</HierarchicalDataTemplate>
<Style x:Key="CountryTemplateItemContainerStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu" Value="{DynamicResource TreeViewContextMenu}"/>
</Style>
<ContextMenu x:Key="TreeViewContextMenu">
<MenuItem .../>
</ContextMenu>
As you can see, you can add your contextmenu in the Itemcontainerstyle of the HierarchicalDataTemplate
Basically I came up with this
<HierarchicalDataTemplate x:Key="ChildTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" CommandParameter="{Binding CopyTag}">
</MenuItem>
<MenuItem Header="Paste" CommandParameter="{Binding PasteTag}">
</MenuItem>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CopyPaste}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</StackPanel.ContextMenu>
<Image Source="/Images/Child.png" Stretch="None" VerticalAlignment="Center" HorizontalAlignment="Center" Style="{StaticResource TreeIconStyle}"/>
<TextBlock Text="{Binding Path=Label}" Style="{StaticResource TreeTextStyle}" ToolTip="{Binding Path=Description}" Tag="{Binding Path=Tag}">
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
And have separate parameters for copy and paste to differentiate copy and paste in a single command.
I have the following code. I am trying to bind the Command property of the MenuItem to the relevant command which is implemented in the DataContext of the user control. Can anyone help me? The command does not get executed.
<UserControl x:Class="MIB2.App.Presentations.Views.CategoryManView"
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:model="clr-namespace:MIB2.Models;assembly=MIB2"
xmlns:local="clr-namespace:MIB2.App.Presentations.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="335">
<UserControl.Resources>
<local:DatabindingDebugConverter x:Key="debugConverter" />
</UserControl.Resources>
<StackPanel Width="417">
<TreeView x:Name="tree"
ItemsSource="{Binding RootCategories}" SelectedItemChanged="TreeView_SelectedItemChanged"
MouseRightButtonDown="OnPreviewMouseRightButtonDown">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Root Category" Command="{Binding AddRootCategoryCommand}"/>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="model:Item"
ItemsSource="{Binding Children}">
<Grid>
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType=TextBox}}">
<MenuItem Header="Add Category" Command="{Binding Path=AddEntityCommand}" />
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" Tag="{Binding DataContext, ElementName=tree}"/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Orientation="Horizontal">
<Button Content="Add New Category" Command="{Binding AddEntityCommand}" />
<Button Content="Add New Root Category" Command="{Binding AddRootCategoryCommand}" />
<Button Content="Delete Category" Command="{Binding DeleteEntityCommand}" />
</StackPanel>
<ContentControl Content="{Binding EntityViewModel.View}" />
</StackPanel>
</UserControl>
Please try this:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="model:Item"
ItemsSource="{Binding Children}">
<Grid Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Category"
Command="{Binding Path=PlacementTarget.Tag.AddEntityCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
You can use RelativeSource in binding to lookup the parent object
e.g
<MenuItem Header="Add Root Category" Command="{Binding DataContext.CommandName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}/>
I have written following xaml code:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="200">
<StackPanel>
<ListBox Name="listBox">
<ListBoxItem Content="item1" />
<ListBoxItem Content="item2" />
<ListBoxItem Content="item3" />
</ListBox>
<TextBlock DataContext="{Binding ElementName=listBox, Path=SelectedItem}" Text="{Binding Content}" ToolTip="{Binding Content}" />
<TextBlock DataContext="{Binding ElementName=listBox, Path=SelectedItem}" Text="{Binding Content}">
<TextBlock.ToolTip>
<ToolTip Content="{Binding Content}"/>
</TextBlock.ToolTip>
</TextBlock>
</StackPanel>
</Window>
Why first tooltip always has the same text as it's parent TextBlock and second never changes it's content when I change selected item in listBox?
In the second example you're setting the ToolTip of the TextBlock to be a ToolTip. So you actually set the Content of the ToolTip to be another ToolTip. These would be the same as the first.
<TextBlock DataContext="{Binding ElementName=listBox, Path=SelectedItem}" Text="{Binding Content}" ToolTip="{Binding Content}" />
<TextBlock DataContext="{Binding ElementName=listBox, Path=SelectedItem}" Text="{Binding Content}">
<TextBlock.ToolTip>
<Binding Path="Content"/>
</TextBlock.ToolTip>
</TextBlock>
And these would pretty much be the same as the last
<Window.Resources>
<ToolTip x:Key="MyToolTip" Content="{Binding Content}"/>
</Window.Resources>
<TextBlock DataContext="{Binding ElementName=listBox, Path=SelectedItem}" Text="{Binding Content}" ToolTip="{Binding Source={StaticResource MyToolTip}}" />
<TextBlock DataContext="{Binding ElementName=listBox, Path=SelectedItem}" Text="{Binding Content}">
<TextBlock.ToolTip>
<ToolTip Content="{Binding Content}"/>
</TextBlock.ToolTip>
</TextBlock>