ContextMenu using MVVM (ItemsSource) trying to find DataContext - wpf

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>

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.

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>

How to bind a property that placed in Viewmodel to each row of Wpf DataGrid?

I need something like EllementStyle and EdititngElementStyle for DataGridTemplateColumn,becasue i want to bind a property to each row of datagrid,and this property is inside ViewModel,i have an instance of this property in viewmodel but need to bind difrenent instance of that to each row!(this property is not in DataGrid.ItemSource)
i did something like this for DataGridTextColumn using EllementStyle and EdititngElementStyle,but it seems DataGridTemplateColumn dosen't have this properties?
any idea?
Edit:
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DataContext.MYProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
i did something like this before for DataGridComboBoxColumn and it works.
<DataGridComboBoxColumn
DisplayMemberPath="Name" SelectedValuePath="ID"
SelectedValueBinding="{Binding DocKindID}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.DocKindList, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.DocKindList, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

Dynamic StringFormat in WPF

I have the following TextBlock Style:
<Style TargetType="TextBlock" x:Key="MyValues">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="Foreground" Value="DarkBlue"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMetric}" Value="True">
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="F1">
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsMetric}" Value="False">
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="F3">
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
</Style.Triggers>
I then use TextBlocks as follows:
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Breadth}" Style="{StaticResource MyValues}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Depth}" Style="{StaticResource MyValues}"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Path=Area}" Style="{StaticResource MyValues}" />
The intention is to set the StringFormat depending on a bound property IsMetric. The Binding in the style are left empty because i want to apply the same style for multiple TextBlocks all bound to different properties. The triggers are working but the StringFormat` is ignored, any ideas?
Here you set Text property to be different things in TextBlock declaration and in DataTriggers. In the first case it's an instance of Binding class. In the second case it's an instance of MultiBinding class. Finally it is one of these. It cannot be both at the moment.
The following markup
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="F1">
</MultiBinding>
</Setter.Value>
</Setter>
instantiates MultiBinding class instance and sets it to Text property.
The Text="{Binding Path=Breadth}" instantiates Binding class instance and sets it to Text property.

Resources