I have such a XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFPlayground"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="80*"/>
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem
Background="Green"
Header="First"/>
<MenuItem
Background="Yellow"
Header="Second"/>
</Menu>
</Grid>
</Window>
When I run it I got such a result
I need to set these items equally inside the Menu
, like the First button takes 50% and the
Secondone also takes 50%, horizontally and vertically respectively. And text
Firstand
Second` also should take a place at the center of the button.
Whatever I have done nothing is helps, I can set fix height or weight, but there is no way to set something like
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
It is just doesn't work
P.S. I know there are other possible approaches something like using another view, but I need to use Menu
which should contain 4 or 5 items... (Just for this example I build Menu example where it is 2)
UPD
Following @BionicCode answer there is what I got
<Window x:Class="PlaygroundWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PlaygroundWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type Menu}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Menu}">
<Border BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}" Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}" Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}" Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<WrapPanel ItemWidth="200"
ClipToBounds="True"
IsItemsHost="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="60*"/>
</Grid.RowDefinitions>
<Menu Grid.Row="1">
<MenuItem
Background="Green"
Header="First"/>
<MenuItem
Background="Yellow"
Header="Second"/>
</Menu>
</Grid>
</Window>
There are three issues
- In the example I have two items and they should take equally entire space horizontally
- In the example I have two items and they should take equally entire space vertically (according to the grid 20%)
- Text on the items (first, second) should be centered, currently, it is aligned left
UPD2
Currently my XAML implementation is:
<Window x:Class="PlaygroundWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PlaygroundWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type Menu}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Menu}">
<Border BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}" Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}" Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}" Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<UniformGrid
ClipToBounds="True"
IsItemsHost="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="60*"/>
</Grid.RowDefinitions>
<Menu Grid.Row="1">
<MenuItem HorizontalContentAlignment="Center"
Background="Green"
Header="Top Level Item" />
<MenuItem HorizontalContentAlignment="Center"
Background="Yellow"
Header="Top Level Header">
<MenuItem HorizontalContentAlignment="Center" Header="Submenu Item" />
<MenuItem HorizontalContentAlignment="Center" Header="Submenu Header">
<MenuItem HorizontalContentAlignment="Center" Header="Submenu Item" />
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</Window>
and it looks like this
BUT if I add one more item
- The desired result is: the item will be added to the same line like the third item in the sequence and take the space equally
- Actual result (see screenshot below) the item moves on the next line
Actually, there are two issues: the first as I described that item moves on the next line and the second one is that text on the item itself is not centered (it alight on the left instead).
It is problems that I am trying to explain from the beginning of the question.
CodePudding user response:
You have to override the default template for the Menu
and replace the items host.
The default host is a StackPanel
which always assigns the mimimum required space to each child.
To automatically spreadout the available space evenly, you can use the UniformGrid
or the WrapPanel
instead
The following Style
is taken from Microsoft Docs: Menu Styles and Templates. but you can easily extract the style using the XAML Designer or Blend.
<Style TargetType="{x:Type Menu}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Menu}">
<Border BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}" Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}" Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}" Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<WrapPanel ItemWidth="200"
ClipToBounds="True"
IsItemsHost="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
To center the content of the MenuItem.Header
you have to override the default template of the MenuItem. Add the following template to a ResourceDictionary
within the scopw of the targeted MenuItem
(e.g., Window.Resources
or App.xaml). Since the template overrides the resource ky, they will apply automatically.
Note that if your top level MenuItem
does not contain child items, you must change the x:Key
identifier of the template to {x:Static MenuItem.TopLevelItemTemplateKey}
.
The pattern to center the MenuItem
content is to find the hosting ContentPresenter
in the corresponding item level template and modify it by bindng the ContentPresenter.HorizontalAlignment
to the templated parent's MenuItem.HorizontalContentAlignment
e.g. by using TemplateBinding
.
After defining the template override you simply have to center the item's content using the MenuItem.HorizontalContentAlignment
property.:
<ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}"
TargetType="{x:Type MenuItem}">
<Border x:Name="Border"
Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter Margin="6,3,6,3"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
ContentSource="Header"
RecognizesAccessKey="True" />
<Popup x:Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsSubmenuOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border x:Name="SubmenuBorder"
SnapsToDevicePixels="True"
BorderThickness="1"
Background="{DynamicResource MenuPopupBrush}">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<ScrollViewer CanContentScroll="True"
>
<StackPanel IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Cycle" />
</ScrollViewer>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation"
Value="true">
<Setter TargetName="Popup"
Property="PopupAnimation"
Value="None" />
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="Border"
Property="BorderBrush"
Value="Transparent" />
<Setter Property="Background"
TargetName="Border">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{StaticResource ControlLightColor}" />
<GradientStop Color="{StaticResource ControlMouseOverColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger SourceName="Popup"
Property="AllowsTransparency"
Value="True">
<Setter TargetName="SubmenuBorder"
Property="CornerRadius"
Value="0,0,4,4" />
<Setter TargetName="SubmenuBorder"
Property="Padding"
Value="0,0,0,3" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground">
<Setter.Value>
<SolidColorBrush Color="{StaticResource DisabledForegroundColor}" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Example
The MenuItem.Header
property value of the items is chosen to give you a hint which template/style key you have to chos. Visit Microsoft Docs: Menu and MenuItem ControlTemplate Example to find the required styles and templates and the required x:Key
identifiers. You can modify them (like I did by replacing the StackPanel
of the Menu
style with a WrapPanel
or UniformGrid
).
Add the styles and templates to a ResourceDictionary within the scope of the Menu (e.g., App.xaml).
<Menu>
<MenuItem HorizontalContentAlignment="Center" Header="Top Level Item" />
<MenuItem HorizontalContentAlignment="Center" Header="Top Level Header">
<MenuItem HorizontalContentAlignment="Center" Header="Submenu Item" />
<MenuItem HorizontalContentAlignment="Center" Header="Submenu Header">
<MenuItem HorizontalContentAlignment="Center" Header="Submenu Item" />
</MenuItem>
</MenuItem>
</Menu>