WPF DataTemplate bind ContextMenu property to local value - wpf

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.

Related

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>

DataGridComboBoxColumn selectItem binding issue

I am trying to create a ComboBox inside a DataGridand to do selectItem binding but when I write this code ,and I select an item in on ComboBox in the column, all the ComboBox items in the DataGrid's column are binding and shows the same selected item. I need to bind each ComboBox item with its selectedItem.
I would be happy to get an help.
this is my code:
<DataGridComboBoxColumn Header="CHOOSE" Width="0.7*"
DisplayMemberPath="Name" SelectedItemBinding="{Binding Path=SelectedReceiver,{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.RxList}" />
<Setter Property="ItemTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Path= Name}" Style="{StaticResource GroupBoxHeaderTextBlockStyle}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.RxList }" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Path= Name}" Style="{StaticResource GroupBoxHeaderTextBlockStyle}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
You bind to DataGrid.ItemsSource.SomeList when you actually need to bind to DataGrid.ItemsSource[x].SomeList.
Basically your list and selected item will always be the same for each column. You can use a static list for your item-model-class (if the list stays the same) and a property for the SelectedItem.
<DataGridComboBoxColumn SelectedItemBinding="{Binding SelectedReceiver}"> <!-- Bind to current item's SelectedReceiver -->
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=(modelNamespace:MyModel.RxList)}"/> <!-- Bind to static list property -->
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>

Command Binding Failure in MenuItem

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>

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>

Automatically focusing parts of a ListBoxItem on selection

I have a ListBox which is populated from a collection of ViewModels, which uses in place editing, which I do by having a couple of styles which I can apply to parts of the DataTemplate which make them visible/collapsed as required. These look something like:
<Style
x:Key="UnselectedVisibleStyle"
TargetType="{x:Type FrameworkElement}">
<Setter
Property="Visibility"
Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Converter={StaticResource boolToVis}, ConverterParameter=False}" />
</Style>
<Style
x:Key="SelectedVisibleStyle"
TargetType="{x:Type FrameworkElement}">
<Setter
Property="Visibility"
Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Converter={StaticResource boolToVis}, ConverterParameter=True}" />
</Style>
With my ListBox having it's ItemTemplate given by something like:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding Name}"
Style="{StaticResource UnselectedVisibleStyle}" />
<TextBox
x:Name="textBox"
Text="{Binding Name}"
Style="{StaticResource SelectedVisibleStyle}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
This works fine, but what I want ideally is to have the TextBox automatically selected when a user clicks the item, ideally in a nice generic way I can use throughout my project, and without too much messing around in my codebehind.
Thanks,
DM.
The following change to your selected Style seemed to work for me:
<Style x:Key="SelectedVisibleStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Converter={StaticResource boolToVis}, ConverterParameter=True}"/>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>

Resources