WPF XAML MenuItem Styles

I recently began working with WPF for the first time.  I was doing this outside of my job on my own time (which is sometimes quite limited).  I had never worked with WPF before this venture.  I decided to download a trial copy of Microsoft Expression Blend 2 to check out the designer side of WPF.  The first thing that I noticed was the menus.  The menus had a black background with white text and the submenus had white borders and white menu separators (pictured below).  I decided that I liked this look and set out to implement this using Visual Studio 2008.  This entry describes how to create this menu.

I implemented the menu using two custom Styles.  The first for the Separator objects and the second for the MenuItem objects.  The XAML for the Separator style is as follows:

<Style x:Key="MLB_Separator" TargetType="{x:Type Separator}">
    <Setter Property="Margin" Value="0,3,0,3" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Separator}">
                <Grid>
                    <Rectangle Height="1" Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Menu}}}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you can see in the code, The Separator style simply sets the default margins (line 2) and draws a 1px high rectangle using the parent Menu’s foreground color (line 7). For the MenuItem style, I started with the SimpleMenuItem style that was included when I was using Expression Blend. I found that this style was not complete and simply did not work correctly in some cases. After cleaning up that code and adding some additional features, I ended up with the XAML below:

<Style x:Key="MLB_MenuItem" TargetType="{x:Type MenuItem}">
    <Setter Property="Foreground" Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Menu}}}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MenuItem}">
                <Border x:Name="Border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition x:Name="Col0" MinWidth="17" Width="Auto" SharedSizeGroup="MenuItemIconColumnGroup"/>
                            <ColumnDefinition Width="Auto" SharedSizeGroup="MenuTextColumnGroup"/>
                            <ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIGTColumnGroup"/>
                            <ColumnDefinition x:Name="Col3" Width="14"/>
                        </Grid.ColumnDefinitions>

                        <!-- ContentPresenter to show an Icon if needed -->
                        <ContentPresenter Grid.Column="0" Margin="4,0,6,0" x:Name="Icon" VerticalAlignment="Center" ContentSource="Icon"/>

                        <!-- Glyph is a checkmark if needed for a checkable menu -->
                        <Grid Grid.Column="0" Visibility="Hidden" Margin="4,0,6,0" x:Name="GlyphPanel" VerticalAlignment="Center">
                            <Path x:Name="GlyphPanelpath" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,2 L0,4.8 L2.5,7.4 L7.1,2.8 L7.1,0 L2.5,4.6 z" FlowDirection="LeftToRight"/>
                        </Grid>

                        <!-- Content for the menu text etc -->
                        <ContentPresenter Grid.Column="1"
                                          Margin="{TemplateBinding Padding}"
                                          x:Name="HeaderHost"
                                          RecognizesAccessKey="True"
                                          ContentSource="Header"/>

                        <!-- Content for the menu IGT -->
                        <ContentPresenter Grid.Column="2"
                                          Margin="8,1,8,1"
                                          x:Name="IGTHost"
                                          ContentSource="InputGestureText"
                                          VerticalAlignment="Center"/>

                        <!-- Arrow drawn path which points to the next level of the menu -->
                        <Grid Grid.Column="3" Margin="4,0,6,0" x:Name="ArrowPanel" VerticalAlignment="Center">
                            <Path x:Name="ArrowPanelPath" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,0 L0,8 L4,4 z"/>
                        </Grid>

                        <!-- The Popup is the body of the menu which expands down or across depending on the level of the item -->
                        <Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
                            <Border x:Name="SubMenuBorder" BorderBrush="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Menu}}}" BorderThickness="1" Padding="2,2,2,2">
                                <Grid x:Name="SubMenu" Grid.IsSharedSizeScope="True">
                                    <!-- StackPanel holds children of the menu. This is set by IsItemsHost=True -->
                                    <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
                                </Grid>
                            </Border>
                        </Popup>
                    </Grid>
                </Border>

                <!-- These triggers re-configure the four arrangements of MenuItem to show different levels of menu via Role -->
                <ControlTemplate.Triggers>
                    <!-- Role = TopLevelHeader : this is the root menu item in a menu; the Popup expands down -->
                    <Trigger Property="Role" Value="TopLevelHeader">
                        <Setter Property="Padding" Value="6,1,6,1"/>
                        <Setter Property="Placement" Value="Bottom" TargetName="SubMenuPopup"/>
                        <Setter Property="MinWidth" Value="0" TargetName="Col0"/>
                        <Setter Property="Width" Value="Auto" TargetName="Col3"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="Icon"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="GlyphPanel"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="IGTHost"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel"/>
                    </Trigger>

                    <!-- Role = TopLevelItem :  this is a child menu item from the top level without any child items-->
                    <Trigger Property="Role" Value="TopLevelItem">
                        <Setter Property="Padding" Value="6,1,6,1"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel"/>
                    </Trigger>

                    <!-- Role = SubMenuHeader : this is a child menu item which does not have children -->
                    <Trigger Property="Role" Value="SubmenuHeader">
                        <Setter Property="DockPanel.Dock" Value="Top"/>
                        <Setter Property="Padding" Value="0,2,0,2"/>
                    </Trigger>

                    <!-- Role = SubMenuItem : this is a child menu item which has children-->
                    <Trigger Property="Role" Value="SubmenuItem">
                        <Setter Property="DockPanel.Dock" Value="Top"/>
                        <Setter Property="Padding" Value="0,2,0,2"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel"/>
                    </Trigger>
                    <Trigger Property="IsSuspendingPopupAnimation" Value="true">
                        <Setter Property="PopupAnimation" Value="None" TargetName="SubMenuPopup"/>
                    </Trigger>

                    <!-- If no Icon is present the we collapse the Icon Content -->
                    <Trigger Property="Icon" Value="{x:Null}">
                        <Setter Property="Visibility" Value="Collapsed" TargetName="Icon"/>
                    </Trigger>

                    <!-- The GlyphPanel contains the CheckMark -->
                    <Trigger Property="IsChecked" Value="true">
                        <Setter Property="Visibility" Value="Visible" TargetName="GlyphPanel"/>
                        <Setter Property="Visibility" Value="Collapsed" TargetName="Icon"/>
                    </Trigger>

                    <!-- Using the system colors for the Menu Highlight and IsEnabled-->
                    <Trigger Property="IsHighlighted" Value="true">
                        <Setter Property="Background" Value="LightGray" TargetName="Border"/>
                        <Setter Property="Foreground" Value="Black"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="LightGray"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The MenuItem style first sets the foreground color to the parent Menu’s foreground color (line 2). Then comes the Template, which is where it gets interesting. The Template starts with a Border around a Grid. The Grid has 4 ColumnDefinition items. The first is for the icon or the checkbox (depending on the type of MenuItem). The second is for the text of the MenuItem. The third is for the input gesture text (e.g. Ctrl-S). The last is for the arrow, if the MenuItem contains a sub-menu.

The contents of the columns are then defined. First is a ContentPresenter that will display the value of the “Icon” property. Next is a Grid that will hold the checkbox, if needed. These are both displayed in the first column of the main Grid (but not at the same time). After this is another ContentPresenter that will display the value of the “Header” property. Next is another ContentPresenter that will display the value of the “InputGestureText” property. Then there is a Grid that will display the arrow if there is a sub-menu. Finally, there is a Popup that will display the sub-menu (if one is defined).

The remainder of the Style contains the Trigger items that will manipulate what is displayed based on the type of MenuItem (top-level menu item, sub-menu meu item, etc.) that is being rendered. I will leave these items for a future entry.

50 thoughts on “WPF XAML MenuItem Styles”

  1. Thanks a lot for the post, it really clears up what Blend does.
    Just one thing —
    I’ve been trying all day long but I can’t seem to figure out how to implement the submenu backgrounds that you see in Microsoft Windows XP Professional SP2 (ex: On Outlook, Excel, Visual Studio etc).
    I want to recreate the way the submenus have a dark gray gradient behind the icon (seems to exist only in that row) while the title part of the menu has the light gray bg color. If you have any ideas on how to create this effect please drop me a line!

  2. Accelerator v2 is implemented in native code, but ships with a managed wrapper that allows us to use it elegantly from C# and other managed languages as well. The library exposes various types that can be used for creating data-parallel computations that process 1D or 2D arrays of data. In this article, we’ll work with FloatParallelArray, which represents a computation that returns a 1D array of floats (float[] in C#).

  3. Always enjoy reading spot on articles by an author who is obviously knowledgeable on their chosen subject….Now is the perfect time (summer now)to spend some money to grab those fancy dress and accessories! LOL….. Keep up the great work, see you next time

  4. thanks a lot for the post, it really clears up what Blend does.
    Just one thing —
    I’ve been trying all day long but I can’t seem to figure out how to implement the submenu backgrounds that you see in Microsoft Windows XP Professional SP2 (ex: On Outlook, Excel, Visual Studio etc).
    I want to recreate the way the submenus have a dark gray gradient behind the icon (seems to exist only in that row) while the title part of the menu has the light gray bg color. If you have any ideas on how to create this effect please drop me a line!
    thompson44

  5. Nice post !! What i’s looking for is a little bit different.

    We can add an input gesture text. Such as "Ctrl + S" on Save menuitem. In menu it appears as – < Save Ctrl +S >. My app enables save menu item only if save is required by app otherwise link is disabled. When "Save" is disabled, save – text appears as disabled (i mean white color) however the gesture text – "Ctrl + S" appears as it is ( i.e. in black color) . So in all save menu item appears as < Ctrl + S>. Can you suggest something such that gesture text can be disabled too like "save".

    Thanks

  6. I find that readers respond very well to posts that show your own weaknesses, failings and the gaps in your own knowledge rather than those posts where you come across as knowing everything there is to know on a topic. People are attracted to humility and are more likely to respond to it than a post written in a tone of someone who might harshly respond to their comments.

  7. Can you upload a demo project? I am assigning the MenuItem the style you have provided, and the menu just looks as it would look by default (grey background, black text), just the headers are a little bit displaced.

    I need the demo project.

  8. I think it will be more beneficial if you can post some small tutorial or something like that in a separate article so that users can gain much knowledge from that.

  9. I guess the same concepts perhaps applied to somebody that provides no products however only advertising space on their blog. I assume that may really well thought-about as a product.

  10. I read your entire article. and reached the point that it is useful and very informative article. Thanks for sharing this article.

  11. Very important information are included in this post. I think you will more valuable information add this site,So meany meany thanks, carry on

  12. I know this is a very old post but it really did the trick for me. I changed the “checked” glyph to use a rectangle with a DrawingBrush but the concept worked the same. Great post and so much better than other peoples suggestion to rewrite the entire control template with 1,000 lines of XAML.

Leave a Reply to Lady Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.