WPF Data template for Listbox and Context menu - wpf

I am trying to make a list with a context menu using a data template.
So far I have the following code:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Items>
<sys:String>Item 1</sys:String>
<sys:String>Item 2</sys:String>
</ListBox.Items>
</Listbox>
I want to add the following context menu:
<ContextMenu>
<MenuItem Header="Edit" />
<MenuItem Header="Delete" />
</ContextMenu>
I have tried using ListBox.ItemContainerStyle and Setter but I am using Material Design, and when I use a style setter it takes away those material design styles.

Solution:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Text="{Binding}"/>
</StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" />
<MenuItem Header="Delete" />
</ContextMenu>
</StackPanel.ContextMenu>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Items>
<sys:String>Item 1</sys:String>
<sys:String>Item 2</sys:String>
</ListBox.Items>
</Listbox>
Or use BasedOn property with ListBox.ItemContainerStyle.

Related

How can I track which element of ItemsControl caused ContexMenu to open?

In my book editor app I need to display themes and subthemes in an hierarchical order, so I made such an markup:
<StackPanel>
<ScrollViewer>
<Grid ColumnDefinitions="auto, *" RowDefinitions="auto">
<ItemsControl Grid.Column="0" Grid.Row="0" Items="{Binding Book.Themes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Name}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.UpdateSubthemesVisibilityCommand}" CommandParameter="{Binding Name}"/>
<ItemsControl Items="{Binding Subthemes}" IsVisible="{Binding AreSubthemesVisible}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" />
<MenuItem Header="Delete"/>
</ContextMenu>
</ItemsControl.ContextMenu>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" Command="{Binding RenameThemeCommand}" CommandParameter="...How to bind it to button which cause menu to open"/>
<MenuItem Header="Delete"/>
</ContextMenu>
</ItemsControl.ContextMenu>
</ItemsControl>
</Grid>
</ScrollViewer>
</StackPanel>
Maybe markup isn't quite clear, but the point is that I have external ItemsControl and internal one. External one's items have structure: button (theme name) + internal ItemsControl which presents subthemes' names as buttons too. Here is Screenshot:
Each ItemsControl has its own ContextMenu, but I can't bind them properly. I want "Rename" MenuItems to be bind to Button.Content of Button which right click caused menu to open. How should I do this here?
I am using MVVM Avalonia, but I hope in WPF it's working the same way so that I could add WPF hashtag to this question.
You don't need to add ContextMenu to ItemsControl. It needs to be added to the items ItemsControl.
This is done in ItemContainerStyle.
In such a case, in ContextMenu DataContext will contain the element on which the ContextMenu is called.
Example:
<Window.Resources>
<spec:StringCollection x:Key="strings">
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
<sys:String>Third</sys:String>
</spec:StringCollection>
<Style x:Key="ItemStyle" TargetType="ContentPresenter">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<TextBlock Text="{Binding}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<ItemsControl ItemsSource="{StaticResource strings}"
ItemContainerStyle="{StaticResource ItemStyle}"/>

Bind to parent control from within context menu within data template

I'm wanting to bind to a parent control from a context menu within a datatemplate.
Unfortunately I'm restricted to .net 3.5 and can't use the x:reference extension introduced in .net 4.
Below is an example of what I'm trying to do
<Window x:Class="WpfApplication17.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication17"
Name="window">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Car}">
<Rectangle Width="100" Height="100" Fill="Red">
<Rectangle.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Colour}"/>
<MenuItem Header="{Binding ElementName=window, Path=ActualWidth}"/>
</ContextMenu>
</Rectangle.ContextMenu>
</Rectangle>
</DataTemplate>
</Window.Resources>
</Window>
But I get "Cannot find source for binding with reference 'ElementName=window'" error due to the context menu not being part of the visual tree.
Edit :
That works great! .. however, it doesn't seem to work when I use a composite collection such as the following
<Window.Resources>
<DataTemplate DataType="{x:Type local:Car}">
<Rectangle Width="100" Height="100" Fill="Red"
Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<Rectangle.ContextMenu>
<ContextMenu>
<ContextMenu.ItemsSource>
<CompositeCollection>
<MenuItem Header="{Binding Colour}"/>
<MenuItem Header="{Binding Path=PlacementTarget.Tag.ActualWidth, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</CompositeCollection>
</ContextMenu.ItemsSource>
</ContextMenu>
<!--<ContextMenu>
<MenuItem Header="{Binding Colour}"/>
<MenuItem Header="{Binding Path=PlacementTarget.Tag.ActualWidth, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>-->
</Rectangle.ContextMenu>
</Rectangle>
</DataTemplate>
</Window.Resources>
Please try this:
<DataTemplate DataType="{x:Type local:Car}">
<Rectangle Width="100" Height="100" Fill="Red"
Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<Rectangle.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Colour}"/>
<MenuItem Header="{Binding Path=PlacementTarget.Tag.ActualWidth, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Rectangle.ContextMenu>
</Rectangle>
</DataTemplate>
See my answer here too.

WPF Datagrid dgDataGrid_Loaded event triggers more than once

I have a wpf dadatagrid and every time the contents being loaded the dgDataGrid_Loaded event triggers twice.
I am not sure whether this is WPF bug or I have missed something!?
here is my Xaml code:
<my:DataGrid Name="dgDataGrid" DockPanel.Dock="Top"
AutoGenerateColumns="False" ClipboardCopyMode="ExcludeHeader"
CanUserDeleteRows="True"
SelectionMode="Extended" SelectionUnit="FullRow"
FontFamily="Tahoma"
ItemsSource="{Binding}"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="True"
EnableRowVirtualization="false"
EnableColumnVirtualization="False"
IsSynchronizedWithCurrentItem="True"
BorderBrush="Blue"
RowBackground="White"
HorizontalGridLinesBrush="Blue"
GridLinesVisibility="Horizontal" VerticalGridLinesBrush="Blue" IsTextSearchEnabled="False"
IsTabStop="True" HeadersVisibility="All"
Loaded="dgDataGrid_Loaded" ContextMenuOpening="dgDataGrid_ContextMenuOpening"
LoadingRow="dgDataGrid_LoadingRow">
<my:DataGrid.Resources>
</my:DataGrid.Resources>
<my:DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type my:DataGridRow}}, Path=Header}"></TextBlock>
</DataTemplate>
</my:DataGrid.RowHeaderTemplate>
<my:DataGrid.ColumnHeaderStyle>
<Style TargetType="my:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Blue"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</my:DataGrid.ColumnHeaderStyle>
<my:DataGrid.ContextMenu>
<ContextMenu Name="cmDataGrid" StaysOpen="True">
<MenuItem Name="mnuView" Header="نمایش">
<MenuItem Name="mnuHideColumn" Header="Hide Column" Click="mnuHideColumn_Click"/>
<MenuItem Name="mnuShowColumn" Header="Show Column"/>
<Separator/>
<MenuItem Name="mnuGroupByColumn"
Header="Group by this column" Click="mnuGroupColumn_Click" />
<MenuItem Name="mnuClearGroups"
Header="Clear grouping" Click="mnuGroupColumn_Click" />
<Separator/>
<MenuItem Header="Header Alignment">
<MenuItem Name="mnuHeaderCenter" Header="Center"/>
<MenuItem Name="mnuHeaderLeft" Header="Left"/>
<MenuItem Name="mnuHeaderRight" Header="Right"/>
</MenuItem>
<MenuItem Header="Content Alignment">
<MenuItem Name="mnuContentCenter" Header="Center"/>
<MenuItem Name="mnuContentLeft" Header="Left"/>
<MenuItem Name="mnuContentRight" Header="Right"/>
</MenuItem>
</MenuItem>
</ContextMenu>
</my:DataGrid.ContextMenu>
</my:DataGrid>
Any suggestions?

WPF : InputBindings on a StackPanel

I want to put a command on a ListBoxItem. The ListBoxItem use a DataTemplate composed of a StackPanel (containing an Image and a TextBlock, both using Binding). I want that the doubleclick on that ListBoxItem fire the command.
I have tried this :
<DataTemplate>
<StackPanel>
<StackPanel.Resources>
<CommonUI:CommandReference x:Key="DoubleClickCommand" Command="{Binding Path=DefaultCommand}" />
</StackPanel.Resources>
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}" />
</StackPanel.InputBindings>
<Image Source="{Binding Path=Thumbnail, IsAsync=True}" IsHitTestVisible="False"/>
<TextBlock Text="{Binding Path=Name}" IsHitTestVisible="False">
</StackPanel>
</DataTemplate>
I have also tried to put the Command Resources on a StackPanel containing this StackPanel, without any change.
I am certain of my binding because when I put the InputBindings part on the TextBlock, it works.
Thanks
Try handling the event in the ListBox instead of the StackPanel:
<ListBox>
<ListBox.Resources>
<CommonUI:CommandReference x:Key="DoubleClickCommand" Command="{Binding Path=DefaultCommand}" />
</ListBox.Resources>
<ListBox.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}" />
</ListBox.InputBindings>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Path=Thumbnail, IsAsync=True}" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox>
My code finally looks like this :
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<CommonUI:CommandReference x:Key="DoubleClickCommand" Command="{Binding Path=DefaultCommand}" />
</StackPanel.Resources>
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{StaticResource DoubleClickCommand}" />
</StackPanel.InputBindings>
<Image Source="{Binding Path=Thumbnail, IsAsync=True}" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
Thanks anyway, Mr Poulin.

WPF MenuItem Header and HeaderTemplate

I want to bind a list of KeyValuePair to a list of MenuItems.
I thought I should use MenuIten.HeaderTemplate, but it didn't work. I only got blank headers.
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem>
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate> </MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
Then I replaced MenuItem.HeaderTemplate with MenuItem.Header, it worked.
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem>
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="2,0,0,0"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
Can anyone explain to me why HeaderTemplate doesn't work here?
Micah is correct. In the first approach I told the menu item how to template itself but never told it what data it binds to! The following works:
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}">
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
Because the HeaderTemplate doesn't have access to the data being bound to the menu item.
The purpose of the Template is to add some elements to the VisualTree. DataTemplate is used for the sub-items ([Sub]MenuItem, ListBoxItem in ListBox, and so on) and is applied to the items holder, it is contrary to the ControlTemplate, wich is applied to the control itself.
What you actually did by this
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
is telling "I want take MenuItem content and Insert the data, wich must be visualized". And then insert this insted dots:
<MenuItem Header="{Binding}">... </MenuItem>
So you are inserting additional menu item to the currently iterating menu item. I can't see the point.
Next is more clear:
<MenuItem Header="Template" ItemsSource="{Binding Samples}">
<MenuItem.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding SomeCommand}" />
</Style>
</MenuItem.Resources>
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
The HeaderTemplate definition should be a DataTemplate, not direct UI content :
...
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate>
...

Resources