Nested MenuItem in WPF ToolBar

One of the most common questions asked on various WPF Forums is “How can we have Submenu or nested menuitems in WPF Toolbar”. Toolbar in WPF doesn’t show sub menuitems on mouseover or click event. So following code for Toolbar doesn’t work.

<ToolBar Name="ToolBar1" Height="25">
    <!-- Regular Items -->
    <Button Content="Item A"/>
    <Button Content="Item B"/>
    <!-- SubItems Menu For Special Items -->
    <MenuItem Header="Special Items">
        <MenuItem Header="Special Item A" />
        <MenuItem Header="Special Item B" />
    </MenuItem>
</ToolBar>

So I decided to create my own custom control which Inherits MenuItem and can handle mouseover or click event. MenuItem has a property IsSubMenuOpen which we can use to open submenu for MenuItem. Another useful property is StayOpenOnClick, which if we set to False then whenever user clicks outside of MenuItem, submenu will automatically get closed. SubMenuPlacementMode property in below code is to decide the direction in which Sub Menu will get open when we click Left Mouse button.

Imports System.Windows.Controls.Primitives
 
Public Class  ToolBarMenuItem
    Inherits System.Windows.Controls.MenuItem
 
    Shared Sub  New()
        'This OverrideMetadata call tell the system that this element wants to provide a style that is different than its base class.
        'This style is defined in themes\generic.xaml
        DefaultStyleKeyProperty.OverrideMetadata(GetType(ToolBarMenuItem), New  FrameworkPropertyMetadata(GetType(ToolBarMenuItem)))
    End Sub
 
    Public Enum  PlacementMode
        Bottom = 0
        Top = 1
        Right = 2
        Left = 3
    End Enum
 
    Public Shared  ReadOnly SubMenuPlacementModeProperty As DependencyProperty = DependencyProperty.Register("SubMenuPlacementMode", GetType(PlacementMode), GetType(ToolBarMenuItem), New  FrameworkPropertyMetadata(PlacementMode.Bottom))
    Public Property  SubMenuPlacementMode As PlacementMode
        Get
            Return GetValue(SubMenuPlacementModeProperty)
        End Get
        Set(ByVal value As PlacementMode)
            SetValue(SubMenuPlacementModeProperty, value)
        End Set
    End Property
 
    Private Sub  ToolBarMenuItem_Click(ByVal sender As Object, ByVal  e As  System.Windows.RoutedEventArgs) Handles Me.Click
        Me.IsSubmenuOpen = False
    End Sub
 
    Private Sub  ToolBarMenuItem_MouseLeave(ByVal sender As Object, ByVal  e As  System.Windows.Input.MouseEventArgs) Handles Me.MouseLeave
        Me.IsSubmenuOpen = False
    End Sub
 
    Private Sub  ToolBarMenuItem_PreviewMouseLeftButtonDown(ByVal sender As Object, ByVal  e As  System.Windows.Input.MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonDown
        If Me.IsSubmenuOpen = False  Then
            Me.IsSubmenuOpen = True
            Me.StaysOpenOnClick = False
        End If
    End Sub
End Class

We also need to add a following XAML Code which contains Style for our custom control.

<Style TargetType="{x:Type local:ToolBarMenuItem}">
    <Setter Property="Padding" Value="2"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ToolBarMenuItem}">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="HighlightOn">
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="BackgroundGradientOver" Storyboard.TargetProperty="(UIElement.Opacity)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="HighlightOff">
                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="BackgroundGradientOver" Storyboard.TargetProperty="(UIElement.Opacity)">
                            <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </ControlTemplate.Resources>
                <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
 
                        <Rectangle Grid.Column="0" x:Name="BackgroundGradientOver" Stroke="#FF3399FF" StrokeThickness="1"  Opacity="0" Fill="#FFC2E0FF"/>
                        <ContentPresenter Grid.Column="0" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"  x:Name="HeaderHost" RecognizesAccessKey="True" ContentSource="Header" />
 
                        <Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="{TemplateBinding SubMenuPlacementMode}" x:Name="SubMenuPopup" Focusable="false" AllowsTransparency="true" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" VerticalOffset="-3">
                            <Grid x:Name="SubMenu">
                                <Border x:Name="SubMenuBorder" BorderBrush="{DynamicResource ControlBorderBrush}" BorderThickness="1" >
                                    <Border.Background>
                                        <SolidColorBrush Color="White"/>
                                    </Border.Background>
                                </Border>
 
                                <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
                            </Grid>
                        </Popup>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
 
 
                    <Trigger Property="IsSuspendingPopupAnimation" Value="true">
                        <Setter Property="PopupAnimation" Value="None" TargetName="SubMenuPopup" />
                    </Trigger>
 
                    <Trigger Property="AllowsTransparency" SourceName="SubMenuPopup" Value="true">
                        <Setter Property="Margin" Value="0,0,3,3" TargetName="SubMenu" />
                        <Setter Property="SnapsToDevicePixels" Value="true" TargetName="SubMenu" />
                        <Setter Property="BitmapEffect" Value="{DynamicResource PopupDropShadow}" TargetName="SubMenuBorder" />
                    </Trigger>
 
                    <Trigger Property="IsMouseOver" Value="true">
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard="{StaticResource HighlightOff}" x:Name="HighlightOff_BeginStoryboard" />
                        </Trigger.ExitActions>
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource HighlightOn}" />
                        </Trigger.EnterActions>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you can see in the above code, we are using SubMenuPlacementMode property to decide direction of Popup control.

Following is demo code of using ToolBarMenuItem control to allow nested menuitem.

<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:ToolBarMenuItemDemo"
    Title="NestedToolBarItem Demo" Height="350" Width="525" Background="Aquamarine">
    <Grid VerticalAlignment="Top">
        <ToolBar Name="ToolBar1" Height="25" >
                <!--Regular Items-->
            <Button Content="Item A"/>
            <Button Content="Item B"/>
                <!--SubItems Menu For Special Items-->
            <my:ToolBarMenuItem Header="Special Items" VerticalContentAlignment="Center" SubMenuPlacementMode="Bottom">
                <MenuItem Header="Special Item 1" />
                <MenuItem Header="Special Item 2" />
            </my:ToolBarMenuItem>
        </ToolBar>
   </Grid>
</Window>

You can also refer to the following link if you want to customize MenuItem style.

http://msdn.microsoft.com/en-us/library/ms747082(v=vs.85).aspx