WPF MenuItem Header and HeaderTemplate - wpf

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>
...

Related

WPF Data template for Listbox and Context menu

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.

Getting selected item from Treeview

I have a TreeView and want to get the selected item from it.
The Treeview itself is populated not manually, but from data in the code. Since it is populated this way, I'm not sure how to get information out of it.
Here is the XAML:
<TreeView Name="trvFamilies" HorizontalAlignment="Left" Margin="10,10,3,3" Width="340">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:Scene}" ItemsSource="{Binding Characters}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="New" Click="MenuItem_Click"></MenuItem>
<MenuItem Header="Remove" Click="MenuItem_Click_1"></MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
<Image Source="{StaticResource ImageSceneRegular}" Margin="0,0,5,0" Width="64" Height="64"/>
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Characters.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:Character}">
<Border BorderThickness="1" Background="AliceBlue" CornerRadius="8,8,3,3">
<StackPanel Orientation="Horizontal" Margin="4" Background="White">
<Image Source="{Binding Img}" Margin="0,0,5,0" Width="64" Height="64" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text=" (" Foreground="Green" />
<TextBlock Text="{Binding Age}" Foreground="Green" />
<TextBlock Text=" years)" Foreground="Green" />
</StackPanel>
</Border>
</DataTemplate>
</TreeView.Resources>
</TreeView>
It is populated by data from an ObservableCollection which is populated within the code and then assigned to the ItemSource of the treeview (and it works fine).
So let's say I had a TextBlock which sat outside the TreeView and wanted to populate it with information from the selected Character (that's the type which populates the drop down part of the TreeView), I don't know how to do that, be it using XAML only or code behind.
Any help on how to do this would be very much appreciated.

Binding an image in a datatemplate

I'm trying to add a menu to my application, I'd like that my menu header is the currently connected user's picture.
My issue is that every binding works except the one for the image, from what I've understood on the net the DataTemplate I use to replace the menu header by a picture doesn't have access to it's parent Datacontext. I tried to use some stuff like relative source and other tricks but with no luck (certainely related to the fact that I'm new in mvvm and I obviously still have a lot to learn :) )
Here's the code I'm using :
<Grid DataContext="{Binding User}">
<DockPanel>
<Label Content="{Binding Strings.Hello, Source={StaticResource StringLocalizer} }" VerticalAlignment="Center" Padding="0,0,5,0" FontFamily="{DynamicResource Font_Normal}" FontSize="15" Foreground="White"/>
<TextBlock Text="{Binding DisplayName, TargetNullValue='Guest', FallbackValue='Guest'}" VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="{DynamicResource Font_Normal}" FontSize="15" Foreground="White"/>
<Menu Name="LogonMenu" Margin="10,0,0,0" DataContext="{Binding User}">
<MenuItem >
<MenuItem.HeaderTemplate>
<DataTemplate>
<!--<Image Source="{Binding Datacontext.Image,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Application}}, Converter={StaticResource ImageConverter}, FallbackValue={StaticResource DefaultUserIcon}}"/>-->
<Image DataContext="{Binding User}" Source="{Binding Path=Image, Converter={StaticResource ImageConverter}, FallbackValue={StaticResource DefaultUserIcon},Mode=TwoWay}"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
<MenuItem Header="{Binding Strings.SignIn, Source={StaticResource StringLocalizer}}" Width="150" Margin="10,0,0,0" IsEnabled="{Binding Connected, Converter={StaticResource BoolInverterConverter}}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}" Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.SignInCommand}"/>
<MenuItem Header="{Binding Strings.SignOut, Source={StaticResource StringLocalizer}}" Margin="10,0,0,0" IsEnabled="{Binding Connected}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}" Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.SignOutCommand}"/>
<MenuItem Header="{Binding Strings.UserInformation, Source={StaticResource StringLocalizer}}" Margin="10,0,0,0" IsEnabled="{Binding Connected}" Visibility="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
</MenuItem>
</Menu>
</DockPanel>
</Grid>
In my case the image always shows the fallback picture, if someone could help me finding a way to have my user's picture it would be great.

How to use converters without binding paths?

I have a Combobox whose ItemsSource is an ObservableCollection of int values.
My combobox itemtemplate consists on an image and a textblock which content is given by 2 converters.
How can I set this 2 bindings? The following code does not compile:
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding, Converter={StaticResource IntToImageConverter}}" Stretch="None" />
<TextBlock Text="{Binding, Converter={StaticResource IntToStringConverter}}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
You need to remove the , so:
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Converter={StaticResource IntToImageConverter}}" Stretch="None" />
<TextBlock Text="{Binding Converter={StaticResource IntToStringConverter}}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>

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.

Resources