Sunday, January 30, 2011

Nested menuitem in WPF ToolBar

One of the most common question 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 decide 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 click outside of MenuItem, submenu will automatically get closed.

Imports System.Windows.Controls.Primitives

Public Class ToolBarMenuItem
Inherits System.Windows.Controls.MenuItem

Shared Sub New()
'This OverrideMetadata call tells 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 XAML Code which contain Style for our custom control which is available in the source at the end of the article.

We can use the above custom control in following way.


<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>

Download Source Code