Command Binding Failure in MenuItem - wpf

I have the following MenuItem template in a resource dictionary
<Style x:Key="RecentMenuItem"
TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.LoadRecentItemCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Header}"/>
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}. {1}">
<Binding Path="(ItemsControl.AlternationIndex)"
RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}"/>
<Binding Path="FullFileName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
This is used like
<Menu>
<MenuItem Header="FILE">
...
<MenuItem Header="_Recent Studies"
ItemsSource="{Binding RecentFiles}"
AlternationCount="{Binding Path=Items.Count,
Mode=OneWay,
RelativeSource={RelativeSource Self}}"
ItemContainerStyle="{StaticResource RecentMenuItem}"/>
<MenuItem/>
The binding on the Command is not working (I can see this with Snoop[dog]).
What is wrong with the above command binding and how can I fix it?
Thanks for your time.

I tried this and it worked just fine... i got the full file name in my command parameter: Here command is defined in my window's VM, so you will have to update it accordingly (if you used usercontrol).
<Menu>
<MenuItem Header="_Recent Studies"
ItemsSource="{Binding Files}"
ItemContainerStyle="{StaticResource RecentMenuItem}"/>
</Menu>
<Style x:Key="RecentMenuItem"
TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Command" Value="{Binding DataContext.MyCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<Setter Property="CommandParameter" Value="{Binding FullName}"/>
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<Binding Path="FullName"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

Related

WPF DataTemplate bind ContextMenu property to local value

I have a problem with binding local values to ContextMenu placed in DataContext of ListViewItem. It only works for ListViewItem directly.
Here is my code:
This is SideBarPlayList UserControl:
<UserControl
xmlns:local="clr-namespace:chkam05.MyApp.Controls"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/Styles/Components/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Style for components:ExtendedListView -->
<Style x:Key="PlayList_ExtendedListViewItemStyle" BasedOn="{StaticResource ExtendedListViewItemStyle}" TargetType="{x:Type components:ExtendedListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="PlayListItem_DoubleClick"/>
<Setter Property="Foreground" Value="{Binding Path=Configuration.ForegroundColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
<Setter Property="HoveredBackground" Value="{Binding Path=Configuration.AccentHoveredColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
<Setter Property="SelectedActiveBackground" Value="{Binding Path=Configuration.AccentSelectedColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
<Setter Property="SelectedInactiveBackground" Value="{Binding Path=Configuration.SelectedInactiveColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
</Style>
<!-- Styles for ContextMenu. -->
<Style x:Key="ControlItem_ExtendedContextMenuStyle" BasedOn="{StaticResource ExtendedContextMenuStyle}" TargetType="{x:Type components:ExtendedContextMenu}">
<Setter Property="Background" Value="{Binding Path=Configuration.ThemeDarkerColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
<Setter Property="BorderBrush" Value="{Binding Path=Configuration.AccentColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
<Setter Property="Foreground" Value="{Binding Path=Configuration.ForegroundColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
</Style>
<Style x:Key="ControlItem_ExtendedContextMenuItemStyle" BasedOn="{StaticResource ExtendedContextMenuItemStyle}" TargetType="{x:Type components:ExtendedContextMenuItem}">
<Setter Property="HoveredBackground" Value="{Binding Path=Configuration.AccentHoveredColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
<Setter Property="Foreground" Value="{Binding Path=Configuration.ForegroundColorBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"/>
</Style>
<!-- Data Templates. -->
<DataTemplate x:Key="NowPlayingDataTemplate">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Kind={Binding IconKind}
Height="32"
Width="auto"/>
<TextBlock
Margin="8,0"
Text="{Binding FileName}"
VerticalAlignment="Center"/>
<StackPanel.ContextMenu>
<components:ExtendedContextMenu
Style="{StaticResource ControlItem_ExtendedContextMenuStyle}">
<components:ExtendedContextMenuItem
Header="Play"
Icon="Play"
Style="{StaticResource ControlItem_ExtendedContextMenuItemStyle}"
Click="ItemExtendedContextMenuItemPlay_Click"/>
<components:ExtendedContextMenuItem
Header="Remove"
Icon="Trash"
Style="{StaticResource ControlItem_ExtendedContextMenuItemStyle}"
Click="ItemExtendedContextMenuItemRemove_Click"/>
</components:ExtendedContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<!-- ... -->
<components:ExtendedListView
ItemContainerStyle="{StaticResource PlayList_ExtendedListViewItemStyle}"
ItemsSource="{Binding Player.PlayList.DataContext, Mode=TwoWay}"
ItemTemplate="{StaticResource NowPlayingDataTemplate}"
Grid.Row="1"
Padding="4,0"
Style="{StaticResource ExtendedListViewStyle}"/>
<!-- ... -->
</Grid>
</UserControl>
{Binding
Path=Configuration.ForegroundColorBrush,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SideBarPlayList}}}"
It works for extended ListViewItem and in ContextMenu that is not placed in DataTemplate. But for ContextMenu that is not placed in DataTemplate I used direct Binding to local value with setting Path only.
All classes have:
static ExtendedListView()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ExtendedListView),
new FrameworkPropertyMetadata(typeof(ExtendedListView)));
}
// Same as ExtendedListViewItem and ExtendedContextMenuItem
static ExtendedContextMenu()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ExtendedContextMenu),
new FrameworkPropertyMetadata(typeof(ExtendedContextMenu)));
}
And I'm not using
<Setter Property="OverridesDefaultStyle" Value="True"/>
It didn't work anyway.

MVVM Command binding for contextemenu of TreeView Element

I need to bind a command (RelayCommand) of my ViewModel with the click event on an element of the conetxtmenu for a treeviewitem
ViewModel Command
private RelayCommand _myElementCommand;
public RelayCommand MyCommand
{
get
{
return _myElementCommand?? (_myElementCommand= new RelayCommand(
x =>
{
//LoadData();
MessageBox.Show("Clicked!");
}));
}
}
VIEW
<TreeView x:Name="tvUBR" ItemsSource="{Binding UBRList, UpdateSourceTrigger=PropertyChanged}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{ Binding Description }">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMatch}"
Value="True">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<TextBlock.ContextMenu>
<ContextMenu>
<!--<MenuItem Header="Details" Command="{Binding DropedElementCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>-->
<MenuItem Header="Details">
<i:Interaction.Triggers>
<!-- EventName ??? -->
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding Path=PlacementTarget.Tag.DataContext.MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectedUBRElementCommand}"
CommandParameter="{Binding SelectedItem, ElementName=tvUBR}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="b:TreeViewItemBehavior.IsBroughtIntoViewWhenSelected" Value="True" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Any idea how to achieve this?
You could bind the Tag property of the TextBlock to the view model and then bind to the command using the PlacementTarget property of the ContextMenu:
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{ Binding Description }">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=TreeView}}" />
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMatch}" Value="True">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Details"
Command="{Binding PlacementTarget.Tag.MyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>

How to set an Icon in a ContextMenu's MenuItem when using ItemContainerStyle

So I have a Context Menu And a MenuItem in it which breaks out into a list of names:
<ContextMenu>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value={Binding Name}/>
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
<Setter Property="CommandParameter" Value="{Binding }" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
Now the above code works file and displays my list of names. Now I would like to add an Icon next to each Name using a Pack URI.. So from this question I can see the best way to do it is to template the Header so I tried that first like the question
<ContextMenu>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header">
<Setter.Value>
<StackPanel>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
<Setter Property="CommandParameter" Value="{Binding }" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
but this gave me the error:
Specified element is already the logical child of another element.
Disconnect it first.
So I after some research I tried:
<ContextMenu>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
<Setter Property="CommandParameter" Value="{Binding }" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
But now all my names are ControlTemplate and no icon is displayed...
How do I add an icon to a Context Menu's Menu Item through ItemContainerStyle?
EDIT
I've tried:
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Icon">
<Setter.Value>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
</Setter.Value>
</Setter>
And I get an icon rendering but only for the last item in my menu?
The problem is that you cannot use one visual in more then one place. You can do it either by using HeaderTemplate instead of Header, but use DataTemplate instead of ControlTemplate
<Style TargetType="MenuItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<!-- other setters -->
</Style>
Note that this solution will put icon in the content part of MenuItem, not the icon. Another solution is to set Icon property of MenuItem as another Setter but in this case Image needs to
be separate Resource with x:Shared set to false otherwise you'll end up with the same problem where only last item has an icon.
<ContextMenu>
<ContextMenu.Resources>
<Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" x:Key="myMenuIcon" x:Shared="False" />
</ContextMenu.Resources>
<MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Icon" Value="{StaticResource myMenuIcon}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>

ContextMenu using MVVM (ItemsSource) trying to find DataContext

I am struggling to get this binding working on my ContextMenu.
I am using Items Source so each item is of the type frm ItemsSource, so I need to search up the tree to get the correct binding context.
The command I am trying to execute exists within the DataContext of the ContextMenu as you can see it bound here:
DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"
So I attempt to bind to that data context using RelativeSource but no matter what I have tried I always get a binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'CreateNodeCommand' property not found on 'object' ''NodeGraphView' (Name='networkControl')'. BindingExpression:Path=DataContext.CreateNodeCommand; DataItem='ContextMenu' (Name='NodeGraphRightClickMenu'); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
Here is the code:
<ContextMenu x:Name="NodeGraphRightClickMenu" DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}" ItemsSource="{Binding DataContext.PolymorphicTypes}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.CreateNodeCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
<Setter Property="CommandParameter">
<Setter.Value>
<MultiBinding Converter="{convertersDF:Converter_MultiBindingToArray}">
<Binding Path="."></Binding>
<Binding Path="Name"></Binding>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Header">
<Setter.Value>
<Binding StringFormat="Create {0}" Path="Name"></Binding>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.ItemContainerStyle>
Any help much appreciated.
The answer was so obvious. I needed to bind to the DataContext of the DataContext. This was confusing so I have re-written it:
<ContextMenu x:Name="NodeGraphRightClickMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" ItemsSource="{Binding PolymorphicTypes}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.CreateNodeCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
<Setter Property="CommandParameter">
<Setter.Value>
<MultiBinding Converter="{convertersDF:Converter_MultiBindingToArray}">
<Binding Path="."></Binding>
<Binding Path="Name"></Binding>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Header">
<Setter.Value>
<Binding StringFormat="Create {0}" Path="Name"></Binding>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>

DataBinding to RelativeSource Tag

I am trying to use the Tag property as the source of a Binding, but the value is null when it gets to the converter.
What am I doing wrong?
Consumer
<Button Style="{StaticResource AddNewItemButtonStyle}" Tag="blah" />
Binding
<Style x:Key="AddNewItemButtonStyle" BasedOn="{StaticResource blueButtonStyle}"
TargetType="{x:Type Button}">
...
<AccessText Text="{Binding RelativeSource={RelativeSource Self},
Path=Tag, Converter={StaticResource AddNewItemForLabel}}">
</Style>
UPDATE
I added a setter for the ToolTip using the same strategy, and that does work BUT only after the second call to the converter (triggered by mousing over).
Can you see why the binding wouldn't work on the first pass?
Is there some place else besides the Tag that I can use more reliably?
2nd UPDATE
Based on Phil's input I changed my style to the xaml below. Must I add a Template property to the style?
<Style x:Key="AddNewItemButtonStyle" BasedOn="{StaticResource blueButtonStyle}" TargetType="{x:Type Button}">
<Setter Property="resx:ResxExtension.DefaultResxName" Value="Smack.Core.Presentation.Resources.MasterDetail"/>
<Setter Property="Content" >
<Setter.Value>
<StackPanel Orientation="Horizontal">
<Image Source="{resx:Resx ResxName=Smack.Core.Presentation.Resources.MasterDetail, Key=bullet_add}" Stretch="Uniform" />
<AccessText VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag, Converter={StaticResource AddNewItemForLabel}}" />
<ContentPresenter/>
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag, Converter={StaticResource AddNewItemForToolTip}}"/>
<Setter Property="Command" Value="{Binding AddNewItemCommand}" />
</Style>
If you change the xaml in the answer I gave to your other question to
<AccessText Grid.Column="1" VerticalAlignment="Center">
<AccessText.Text>
<MultiBinding StringFormat="{}_{0} {1}">
<Binding Source="{StaticResource Test}"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Tag"/>
</MultiBinding>
</AccessText.Text>
</AccessText>
Then Tag will work.
Or you can use the short form of TemplateBinding
<AccessText Grid.Column="1" VerticalAlignment="Center" Text="{TemplateBinding Tag}"/>
or the long form
<AccessText Grid.Column="1" VerticalAlignment="Center"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag}"/>
or, your style will work like this (bits deleted for testing):
<Style x:Key="AddNewItemButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Content" >
<Setter.Value>
<StackPanel Orientation="Horizontal">
<AccessText VerticalAlignment="Center"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=Tag}" />
<ContentPresenter/>
</StackPanel>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}"/>
</Style>

Resources