I'm trying to populate a MenuItem from my menu item list, in doing so I'm able to get the data properly but the styling is getting messed up, it's as if there is an overlay with my current style and the default menu item style
Please have a look at the images:
This is my list of sample items in the Menu
public IEnumerable<String> MenuItems { get; set; } = new List<string>() { "ItemA", "ItemB" };*
This is the main code
<Border Background="#fff">
<Menu>
<MenuItem Style="{StaticResource MenuItemDropDownBaseStyle}"
Header="Hello"
ItemsSource="{Binding MenuItems}">
<MenuItem.ItemTemplate>
<DataTemplate>
<!-- doesn't work -->
<MenuItem Header="{Binding }" Style="{StaticResource MenuItemBaseStyle}" />
</DataTemplate>
</MenuItem.ItemTemplate>
<!-- works -->
<!-- <MenuItem Header="ItemA" Style="{StaticResource MenuItemBaseStyle}" /> -->
<!-- <MenuItem Header="ItemB" Style="{StaticResource MenuItemBaseStyle}" /> -->
</MenuItem>
</Menu>
</Border>
This is my styling for MenuItem dropdown and each container item within
<Window.Resources>
<Color x:Key="White">#ffffff</Color>
<SolidColorBrush x:Key="BackgroundBrushWhite" Color="{StaticResource White}" />
<Color x:Key="Black">#000000</Color>
<SolidColorBrush x:Key="BackgroundBrushBlack" Color="{StaticResource Black}" />
<Color x:Key="Viking">#67cfdf</Color>
<SolidColorBrush x:Key="BackgroundBrushViking" Color="{StaticResource Viking}" />
<Color x:Key="MidnightBlue">#003966</Color>
<SolidColorBrush x:Key="BackgroundBrushMidnightBlue" Color="{StaticResource MidnightBlue}" />
<Color x:Key="CongressBlue">#014984</Color>
<SolidColorBrush x:Key="BackgroundBrushCongressBlue" Color="{StaticResource CongressBlue}" />
<Color x:Key="DullLavender">#8ca5e0</Color>
<SolidColorBrush x:Key="BackgroundBrushDullLavender" Color="{StaticResource DullLavender}" />
<Style x:Key="MenuItemDropDownBaseStyle" TargetType="MenuItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Grid>
<Border x:Name="ContentBorder" Padding="12, 7, 12, 7" CornerRadius="5" Margin="5">
<ContentPresenter ContentSource="Header"
TextBlock.Foreground="{StaticResource BackgroundBrushBlack}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<Popup x:Name="PART_Popup"
AllowsTransparency="True"
IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
<Border CornerRadius="5"
Width="190"
Padding="0, 5, 0, 5"
Background="{StaticResource BackgroundBrushViking}">
<!--<ScrollViewer Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">-->
<Grid RenderOptions.ClearTypeHint="Enabled">
<!--<Canvas HorizontalAlignment="Center" VerticalAlignment="Center" />-->
<ItemsPresenter x:Name="ItemsPresenter" />
</Grid>
<!--</ScrollViewer>-->
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation" Value="True">
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None" />
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" TargetName="ContentBorder" Value="{StaticResource BackgroundBrushViking}" />
<Setter Property="BorderBrush" TargetName="ContentBorder" Value="{StaticResource BackgroundBrushViking}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MenuItemBaseStyle" TargetType="MenuItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Border x:Name="ItemBorder" Height="32">
<Grid>
<ContentPresenter x:Name="ContentP"
ContentSource="Header"
TextBlock.Foreground="{StaticResource BackgroundBrushWhite}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<Border x:Name="SideHighlight"
Height="{Binding RelativeSource={RelativeSource AncestorType=Border}, Path=Height}"
Width="4"
Background="{StaticResource BackgroundBrushCongressBlue}"
CornerRadius="0, 50, 50, 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Visibility="Collapsed" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="TextBlock.Foreground" TargetName="ContentP" Value="{StaticResource BackgroundBrushMidnightBlue}" />
<Setter Property="Visibility" TargetName="SideHighlight" Value="Visible" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" TargetName="ItemBorder" Value="blue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
I tried applying in the ItemContainerStyle my custom style but doesn't work anyhow, I know I've missed something here but not able to figure out
MenuItem
is a an ItemsControl
(a HeaderdItemsControl
). As such it automatically generates an item container to host the content. The content is the data item.
This container is itself of type MenuItem
. MenuItem
is an item container that can host child item containers.
Your properly behaving example omits the automatic item container generation because you were adding those containers explicitly:
<MenuItem>
<MenuItem /> <!-- Explicit child container -->
</MenuItem>
The "non-working" example defines an DataTemplate
and the child elements are added implicitly via binding items to the MenuItem.ItemsSource
: the parent MenuItem
will generate an item container of type MenuItem
and sets the data item as its content. Then the content's visuals are defined by the DataTemplate
which in your case defines another MenuItem
.
The result is a MenuItem
(item container) that has an MenuItem
(from DataTemplate
) as content. This leads to the undesired layout.
In other words, the problem is not the Style
but the DataTemplate
.
To fix this, remove the MenuItem
from the DataTemplate
. Instead provide an element that hosts the representation of the data item for example a TextBlock
:
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
Although this will make it work, the DataTemplate
itself is pretty much redundant. It only displays a string
value (the source collection is a collection of string
items).
The HeaderedItemsControl
will create a similar content template automatically by default.
Therefore, the following code is sufficient to show a list of menu items as child items of a MenuItem
:
<!-- Will automatically show "Item A" and "Item B" as child menu items -->
<MenuItem Style="{StaticResource MenuItemDropDownBaseStyle}"
Header="Hello"
ItemsSource="{Binding MenuItems}" />
You only have to define a DataTemplate
if the content layout is complex.
To specify a Style
for the chld items explicitly, simply set the ItemsControl.ItemContainer
property:
<!-- Will automatically show "Item A" and "Item B" as child menu items -->
<MenuItem Style="{StaticResource MenuItemDropDownBaseStyle}"
Header="Hello"
ItemsSource="{Binding MenuItems}"
ItemContainerStyle="{StaticResource MenuItemBaseStyle}" />
In case of a more complex data item, you can control the property which should be displayed as the header by setting the ItemsControl.DispalyMemberPath
:
<!-- Will automatically show "Item A" and "Item B" as child menu items -->
<MenuItem Style="{StaticResource MenuItemDropDownBaseStyle}"
Header="Hello"
ItemsSource="{Binding MenuItems}"
ItemContainerStyle="{StaticResource MenuItemBaseStyle}"
DispalyMemberPath="NameOfSomePropertyOnTheItemThatShouldBeTheHeader" />
If your MenuItem
is more complex than showing a simple text header then you have to define a DataTemplate
or define a MenuItem.Header
object using the XAML object notation (<MenuItem.Header> ... </MenuItem.Header>
):
ItemModel.cs
// TODO::Implement INotifyPropertyChanged even if properties won't change!
class ItemModel : INotifyPropertyChanged
{
// Value for the menu item's header
public string Title { get; }
// Value that binds to a CheckBox in the menu item
public bool IsSelected { get; set; }
}
<!--
Dynamic MenuItem generation using data binding (using ItemsSource).
-->
<Menu>
<!--
The Style assigned to ItemContainerStyle property is
automatically applied to all generated child containers (nested MenuItem elements).
Bind ItemsSource to an ObservableCollection<ItemModel>
-->
<MenuItem Header="Parent of Generated Checkable Entries"
ItemsSource="{Binding ItemModels}"
ItemContainerStyle="{StaticResource MenuItemBaseStyle}">
<MenuItem.ItemTemplate>
<DataTemplate DataType="local:ItemModel">
<CheckBox Content="{Binding Title}"
IsChecked="{Binding IsSelected}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
<!-- Static (inline) MenuItem generation by adding MenuItem elements explicitly -->
<Menu>
<!--
The Style assigned to ItemContainerStyle property is
automatically applied to all children (nested MenuItem elements).
If a child MenuItem element has a value assigned to its Style property,
then this local value is used instead of the value from the ItemContainerStyle property.
-->
<MenuItem Header="Parent of Checkable Entries"
ItemContainerStyle="{StaticResource MenuItemBaseStyle}">
<!--
The style assigned to the Style property is
exclusively applied to the current MenuItem.
In addition, this local value overrides the Style value
assigned to the parent's ItemContainerStyle property.
-->
<MenuItem Style="{StaticResource MenuItemBaseStyle}">
<MenuItem.Header>
<CheckBox Content="Explicit menu item"
IsChecked="True" />
</MenuItem.Header>
</MenuItem>
<!-- Alternative declaration using an item model -->
<MenuItem Style="{StaticResource MenuItemBaseStyle}">
<MenuItem.Header>
<local:ItemModel Title="Explicit menu item"
IsSelected="True" />
</MenuItem.Header>
<MenuItem.ItemTemplate>
<DataTemplate DataType="local:ItemModel">
<CheckBox Content="{Binding Title}"
IsChecked="{Binding IsSelected}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</MenuItem>
</Menu>