How to stretch the content of a templated MenuItem using WPF - wpf

in my WPF application I have a Menu defined as follows:
<Menu
Grid.Column="1"
MinWidth="50"
MinHeight="50"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
Visibility="{Binding Path=ShowSynergies, Converter={StaticResource BoolToVisConverter}}">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
BorderBrush="Black"
BorderThickness="1"
ItemsSource="{Binding Path=CurrentlyAvailableSynergies, Source={StaticResource ResourceKey=VM}}">
<MenuItem.Header>
<TextBlock
Margin="5"
FontSize="18"
Text="{Binding Path=ActiveSynergy.Synergy, Source={StaticResource ResourceKey=VM}}"
TextAlignment="Center" />
</MenuItem.Header>
<MenuItem.ToolTip>
<TextBlock Text="{Binding Path=ActiveSynergy.Synergy, Source={StaticResource ResourceKey=VM}}" />
</MenuItem.ToolTip>
<MenuItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type ent:VisualSynergy}" ItemsSource="{Binding Path=AvailableDiameters}">
<TextBlock Text="{Binding SynergyText}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ent:VisualDiameter}">
<TextBlock
VerticalAlignment="Stretch"
Background="Purple"
Text="{Binding Path=VisualValue}">
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="PreviewMouseLeftButtonDown">
<intr:InvokeCommandAction Command="{Binding Path=SelectSynergyCommand}">
<intr:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource ResourceKey=StdMultiConverter}">
<Binding Path="." />
<Binding Path="." Source="{StaticResource ResourceKey=VM}" />
</MultiBinding>
</intr:InvokeCommandAction.CommandParameter>
</intr:InvokeCommandAction>
</intr:EventTrigger>
</intr:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</MenuItem.Resources>
</MenuItem>
</Menu>
Everything is working as intended, all data is bound properly and updated when it should be updated.
My larges issue is that the final TextBlock (the one related to my VisualDiameter type) doe snot stretch to fill the whole MenuItem available space (see screenshot). I've tried also to redefine styles, but since I am inside the MenuItem resources definitions there is something missing and no stretching happens.
Looking ot other questions my issue seems related to the bizzarre definition of TreeView and MenuItem by Microsoft, but I am not confidend enough to start tinkering with basic templates.

You should define an ItemContainerStyle and bind the Command property of the MenuItem container rather than trying to stretch the TextBlock in the DataTemplate:
<MenuItem
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
BorderBrush="Black"
BorderThickness="1"
ItemsSource="{Binding Path=CurrentlyAvailableSynergies}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding SelectSynergyCommand}" />
</Style>
</MenuItem.ItemContainerStyle>
<MenuItem.Header>
...

Related

Dynamically set a property in an Item Template

I set an image path of an Image in a StackPanel used in a GroupItem using the following resource (which as is works fine):
<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Name="expander" IsExpanded="True" >
<Expander.Header>
<StackPanel Orientation="Horizontal">
<Image Source="pack://application:,,,/Resources/History.ico" Margin="2,0"
Width="18" Height="18" ></Image>
<TextBlock Text="{Binding Name}" Padding="2,0"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which is used in this DataGrid:
<DataGrid Name="JobHistory" CanUserAddRows="False" AutoGenerateColumns="False" ColumnWidth="*"
CanUserDeleteRows="False" ItemsSource="{Binding}" Grid.Row="2"
Grid.ColumnSpan="5" CanUserResizeRows="False"
Grid.RowSpan="2" IsTextSearchEnabled="True" VerticalScrollBarVisibility="Visible" >
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Status" Width="Auto" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding ResultImagePath}" Height="18" Width="18"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Job description" Binding="{Binding JobDescription}"/>
</DataGrid.Columns>
</DataGrid>
The DataView is grouped via this code:
ListCollectionView collection = new ListCollectionView(JobData);
collection.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
JobHistory.ItemsSource = collection;
My Question: How can I dynamically set the image Source in the StackPanel?
<StackPanel Orientation="Horizontal">
<Image Source="pack://application:,,,/Resources/History.ico" Margin="2,0"
Width="18" Height="18" ></Image>
<TextBlock Text="{Binding Name}" Padding="2,0"/>
</StackPanel>
Edit 1:
Using:
<UserControl.Resources>
<Image x:Key="image" Source="pack://application:,,,/Resources/History.ico" Height="18" Width="18" Margin="2,0"/>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{StaticResource ResourceKey=image}"/>
Width="18" Height="18" ></Image>
<TextBlock Text="{Binding Name}" Padding="2,0"/>
</StackPanel>
as user2760623 suggested works.
My Problem however remains. At any given time I have multiple rows grouped by "Name". There can also be several different Groups. Depending on the Jobs current Status, I would like to Change the Image in the GroupItem Header. So how do I figure out which header is the "right" Header, and how do I manipulate exactly that one single Header?
Put the image source as a dynamic resource, and then you can change it. Just do the following:
Define the namespace - xmlns:clr="clr-namespace:System;assembly=mscorlib".
Add as resource - <clr:String x:Key="imageSource" >the path...</clr:String>.
And the image itself - <Image Source="{DynamicResource ResourceKey=imageSource}".
And when you want to change it - this.Resources["imageSource"] = "another path...".
You can also do the same concept just put the whole image as a resource (instead of just the image path), than you don't need to add the namespace (number 1 above). And put it as a Content of a ContentControl -
<ContentControl Content="{StaticResource ResourceKey=image}"/>.

How add command to an ElementMenuItem?

I am trying to add a command to an ElementMenuItem, but the command can not be fired.
<Grid Name="MenuGrid">
<s:ElementMenu
Name="MainMenu"
ActivationMode="AlwaysActive"
ActivationHost="{Binding ElementName=MenuGrid}"
ItemsSource="{Binding Menu}">
<s:ElementMenu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Menu}" DataType="{x:Type s:ElementMenuItem}">
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<behaviours:EventToCommand Command="{Binding Source={StaticResource Locator}, Path=NavigatorMenu.SimpleCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Image Source="{Binding ImageUri}"></Image>
<TextBlock Text="{Binding Title}"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
</TextBlock>
</Grid>
</HierarchicalDataTemplate>
</s:ElementMenu.ItemTemplate>
</s:ElementMenu>
</Grid>
Does anyone know how to add a command to the menu? Thank you in advance.
No idea what those controls are but if they inherit from normal menus you should use the ItemContainerStyle to hook up commands:
<s:ElementMenu Name="MainMenu" ActivationMode="AlwaysActive"
ActivationHost="{Binding ElementName=MenuGrid}" ItemsSource="{Binding Menu}">
<s:ElementMenu.ItemContainerStyle>
<Style TargetType="s:ElementMenuItem">
<Setter Property="Command"
Value="{Binding Source={StaticResource Locator}, Path=NavigatorMenu.SimpleCommand}" />
</Style>
</s:ElementMenu.ItemContainerStyle>
<s:ElementMenu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Menu}">
<Grid>
<Image Source="{Binding ImageUri}"/>
<TextBlock Text="{Binding Title}" VerticalAlignment="Bottom"
HorizontalAlignment="Center"/>
</Grid>
</HierarchicalDataTemplate>
</s:ElementMenu.ItemTemplate>
</s:ElementMenu>
This of course assumes that all the bindings actually work, if they don't you should probably debug them...
I don't know why but I find interaction triggers don't work in a Grid. Change your xaml to
<HierarchicalDataTemplate ItemsSource="{Binding Menu}" DataType="{x:Type s:ElementMenuItem}">
<ContentControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<behaviours:EventToCommand Command="{Binding Source={StaticResource Locator}, Path=NavigatorMenu.SimpleCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Image Source="{Binding ImageUri}"></Image>
<TextBlock Text="{Binding Title}"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
</TextBlock>
</Grid>
</ContentControl>
</HierarchicalDataTemplate>

ListView: define ItemsPanelTemplate in resource dictionary

I have a ListView which layout looks like a Windows Explorer view (icon + some details), bound to a list somewhere in the ViewModel.
My aim here is to be able to switch between explorer view or classic view whenever we want.
I could define an ItemsPanelTemplate doing exactly the work to display correctly the layout, directly in the ListView.ItemsPanel field. Now, I'd like to define it in the resources so that I'll be able to use it in different views, and especially in one control, the user should have the choice between Explorer view or classic list view (the default rendering for a list)
How'd you do that? I cannot define any ItemsPanelTemplate in my ResourceDictionary, and if I define a DataTemplate it is not compatible (while I thought that following pure logic, ItemsPanelTemplate should inherit from DataTemplate, but it actually doesn't look like so).
Code snippet for the actual list:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Width="{Binding (FrameworkElement.ActualWidth),
RelativeSource={RelativeSource
AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight,
RelativeSource={RelativeSource AncestorType=ListView}}"
/>
<!--
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
Height="Auto"
Width="150" >
<Image
Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
Margin="5"
Height="50"
Width="50" />
<StackPanel
VerticalAlignment="Center"
Width="90" >
<TextBlock
Text="{Binding Path=Appli.AppName}"
FontSize="13"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock
Text="{Binding Path=Appli.AppType}"
FontSize="9"
HorizontalAlignment="Left"
Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Keeping the ItemTemplate in a static resource was easy to do, but now I can't do anything with the ItemsPanelTemplate...
Any ideas? I'm using MVVM so I'm trying ideally not to use code-behind if possible
You would use a style for the whole ListView for that. So you would do:
<Grid.Resources>
<Style x:Key="ListViewStyle" TargetType="ListView">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel
Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}" />
<!--
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
-->
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ListView
SelectionMode="Single"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Bottom"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding ListUserApps, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding SelectedUserApp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="White"
Style="{StaticResource ListViewStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
Height="Auto"
Width="150">
<Image
Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
Margin="5"
Height="50"
Width="50"/>
<StackPanel
VerticalAlignment="Center"
Width="90">
<TextBlock
Text="{Binding Path=Appli.AppName}"
FontSize="13"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock
Text="{Binding Path=Appli.AppType}"
FontSize="9"
HorizontalAlignment="Left"
Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want the user then be able to switch between explorer and classic view, just define a second Style and switch the style of the listview. This can be done for example with some VisualStates and a 'DataStateBehavior'.
Alternatively you could create a style with some DataTriggers and Setters for the individual ItemsPanels.

Reusing a tooltip for several textboxes

What should I write in the value of the Text property of the tooltip resource so it would show the text of each textblock dynamically ?
<StackPanel x:Name="root">
<StackPanel.Resources>
<ResourceDictionary>
<ToolTip x:Key="tooltiptemplate">
<TextBlock Background="LightBlue" TextTrimming="WordEllipsis" Text="?????"/>
</ToolTip>
</ResourceDictionary>
</StackPanel.Resources>
<TextBlock Text="Mickel" ToolTip="{StaticResource tooltiptemplate}"/>
<TextBlock Text="Kim" ToolTip="{StaticResource tooltiptemplate}"/>
<TextBlock Text="Jenny" ToolTip="{StaticResource tooltiptemplate}"/>
</StackPanel>
{Binding PlacementTarget.Text, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}

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