Notice the menu separator below Print.
This is the next sample in the WPF Sample Applications Series. The purpose of the Sample Series is to provide concise code solutions for specific programming tasks. This sample provides a brief description of the problem, the solution and full source code.
In this WPF Sample, I have adopted the Code Project style of marking code and variable names within the text of the article with the color #990000. Please leave a comment on this styling and let me know if I should keep using it or not. Thank you for your comments and suggestions.
I have a previous post on using the HierarchicalDataTemplate to create a disk explorer that you can read.
In this WPF Sample, I want to demonstrate using the HierarchicalDataTemplate with data drawn from a business object to dynamically populate a WPF Menu Control. A lot of Internet WPF menu examples populate the Menu Control in XAML markup. This is fine for applications with static menus, but what about business applications that need to display menus based on the user or group that the user belongs to?
This coding technique also allows you to have unlimited hierarchical menu nesting. Many WPF Internet examples have hard coded the number of levels their menus can have. When working in the “real world” with business applications, we need to be able to add additional levels to the menu without having to modify the UI code. This code accomplishes that goal.
Dynamically loading menus for business applications also allows you to customize your menu content based not only on the user, but also on the customer. Let’s say that you have an application that spans several vertical areas like, AP, AR, GL, Tax, etc. Some customers many not require all the vertical areas that other customers do. Dynamically loading the menu from a database allows great flexibility for application deployment to various customers.
All my WPF business applications load their menus dynamically after the user logs into the application. I thought I would share this coding technique with you.
Downloads
After downloading, you must rename the following download by changing the file extension from .doc to .zip. This is a requirement of WordPress.com.
HierarchicalDataTemplate
The HierarchicalDataTemplate is a super WPF feature. This data template allows you to take a flat data source, like a collection of menu data items that was populated from a SQL query and render it hierarchically. This process requires very little code.
Big Picture Of The Menu Loading
- Window code calls the business layer and gets the data for the menu.
- The result of this call contains the entire menu stored in a collection.
- This collection is then referenced by the read-only property MenuDataItems on the Window object. This is an important step.
- Menu Control has its DataContext property set to the top level items. In our example the top level items are, File, Administration and Finance.
- When the user opens one of the top level menus the following occurs:
- The sub-items of that menu are then loaded. Notice, the sub-menu items do not load until they are required.
How The HierarchicalDataTemplate Process Data Items
- Menu Control processes one MenuDataItem from its DataContext at a time.
- WPF finds that the MenuDataItem Type has a matching data template that has its DataType property set to MenuDataItem, so it selects this data template for rendering the MenuDataItem.
- HierarchicalDataTemplate process the data item
- Since this data template has a Style Selector, this code runs first.
- A resource in the data template XAML markup, points to a class that derives from StyleSelector.
- In this application, that class is, MenuSeparatorStyleSelector.
- This code gets a reference to the MenuDataItem and if the IsSeparator property is True, it provides a custom style for this MenuItem.
- In this application the resource is, menuSeparatorStyle. This style provides a control template that only has a Separator control in it. This control template is used instead of the control template in our HierarchicalDataTemplate.
- The ContentPresenter.Loaded event fires, calling the OnMenuDataItemLoaded event handler code.
- This code locates the MenuItem in the VisualTree.
- Then gets a reference to the MenuItem.DataContext and casts it to a MenuDataItem.
- The MenuDataItem properties can then be used to populate other MenuItem properties.
- In this application, the MenuDataItem.Icon property is used to add an icon to the MenuItem.
- Note: It is in this event handler that you can populate the MenuItem Command and CommandParameter properties. These are the typical properties used in the WPF RoutedEvent and RoutedCommand handlers to process menu commands.
- The MenuItem ContentPresenter has its content property bound to the MenuDataItem.MenuText property.
- The RecognizedAccessKey property is set to True. This is the secret to allows the MenuDataItem.MenuText property to contain “_File” and the underscore will be hidden until the <ALT> key is pressed.
- Here is the magic. Each item created by the HierarchicalDataTemplate has its own ItemsSource. A collection of child items if you will. The application then uses a binding converter and passes it the MenuDataItem.MenuItemID property.
- The converter code that gets called is the MenuDataItemConverter.
- This converter then gets a reference to the ApplicationMainWindow and runs executes the GetChildItems function, passing the MenuItemID property value.
- The GetChildItems function returns a List(Of MenuDataItem). This list is populated with any MenuDataItem from the MenuDataItems collection where their MenuItemParentID is equal to the MenuItemID that was passed into the converter. In effect, we are simply getting all the children of the MenuItem that was clicked by the user.
- Since this data template has a Style Selector, this code runs first.
- The above looks complex but it’s really not. Just several pieces of code working together to render a multi-level, data driven, dynamic menu with text, icons, separators.
ApplicationMainWindow.xaml
A pattern that I have adopted, I always name my main application window, ApplicationMainWindow.
You can now use the above explanation and follow through this simple XAML.
<Window x:Class="ApplicationMainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:HierarchicalMenuSample" Title="HierarchicalDataTemplate Menu Sample" Height="300" Width="400" > <Window.Resources> <Style TargetType="{x:Type MenuItem}" x:Key="menuSeparatorStyle"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type MenuItem}"> <Separator HorizontalAlignment="Stretch" IsEnabled="false"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <HierarchicalDataTemplate DataType="{x:Type local:MenuDataItem}"> <ContentPresenter Content="{Binding Path=MenuText}" Loaded="OnMenuDataItemLoaded" RecognizesAccessKey="True" /> <HierarchicalDataTemplate.ItemsSource> <Binding Path="MenuItemID"> <Binding.Converter> <local:MenuDataItemConverter /> </Binding.Converter> </Binding> </HierarchicalDataTemplate.ItemsSource> </HierarchicalDataTemplate> <local:MenuSeparatorStyleSelector x:Key="menuSeparatorStyleSelector"/> </Window.Resources> <DockPanel> <Menu x:Name="mnuMainMenu" ItemsSource="{Binding}" VerticalAlignment="Top" DockPanel.Dock="Top" ItemContainerStyleSelector="{StaticResource menuSeparatorStyleSelector}"/> </DockPanel> </Window>
ApplicationMainWindow.xaml.vb
Partial Public Class ApplicationMainWindow Inherits System.Windows.Window #Region " Declarations " Private _objMenuDataItems As List(Of MenuDataItem) #End Region #Region " Properties " Public ReadOnly Property MenuDataItems() As List(Of MenuDataItem) Get Return _objMenuDataItems End Get End Property #End Region #Region " Constructors & Loaded " Public Sub New() InitializeComponent() End Sub Private Sub ApplicationMainWindow_Loaded(ByVal sender As Object, ByVal e As _ System.Windows.RoutedEventArgs) Handles Me.Loaded _objMenuDataItems = MenuItems.Load Me.mnuMainMenu.DataContext = GetChildItems(0) End Sub #End Region #Region " Menu " Private Sub OnMenuDataItemLoaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Dim objMenuItem As MenuItem = FindAncestores.FindAncestorMenuItem(CType(sender, DependencyObject)) If objMenuItem IsNot Nothing Then Dim objMenuDataItem As MenuDataItem = TryCast(objMenuItem.DataContext, MenuDataItem) If objMenuDataItem IsNot Nothing Then If objMenuDataItem.Icon.Length > 0 Then 'You have a number of options here. ' 1. you could assign each menu icon a resource name ' 2. or you could switch the directory based on the skin. ' 3. or ... 'I have hard coded the menu icon directory for this short demo Dim img As Image img = New Image img.Source = New BitmapImage(New Uri(String.Concat("Images\", objMenuDataItem.Icon), _ UriKind.Relative)) objMenuItem.Icon = img objMenuItem.Margin = New Thickness(0, 8, 0, 0) End If 'TODO developers this is where you assign the ' MenuItem Command and CommandParameter ' ' Depending on your application architecture, you'll need to ' expand the MenuDataItem class to support your menu commands. End If End If End Sub 'scope must be Friend so that the MenuDataItemConverter ' can use this function Friend Function GetChildItems(ByVal intParentID As Integer) As List(Of MenuDataItem) Dim obj As New List(Of MenuDataItem) For Each i As MenuDataItem In Me.MenuDataItems If i.MenuItemParentID = intParentID Then obj.Add(i) End If Next Return obj End Function #End Region End Class
MenuSeparatorStyleSelector.vb
This is the StyleSelector code that is used to swap the ControlTemplate for any MenuDataItems that have their IsSeparator property equal to True.
Public Class MenuSeparatorStyleSelector Inherits StyleSelector Public Overrides Function SelectStyle(ByVal item As Object, _ ByVal container As System.Windows.DependencyObject) As System.Windows.Style If DirectCast(item, MenuDataItem).IsSeparator Then Return DirectCast(DirectCast(container, FrameworkElement).FindResource( _ "menuSeparatorStyle"), Style) Else Return MyBase.SelectStyle(item, container) End If End Function End Class
After you download the source, step through the code as it runs. You’ll be able to see how the pieces fit together.
Hope you can learn just a little bit more about WPF from this article and the Sample Series.
Just a grain of sand on the worlds beaches.



November 9, 2009 at 8:24 pm |
I enjoyed the article, this is exactly what I need. Any chance you have this with C# code?
November 9, 2009 at 8:55 pm |
Jim,
Sorry, just VB. However you can use this converter, http://www.developerfusion.com/tools/convert/vb-to-csharp/
The converter goes both ways.
Glad you like the blog post.
Cheers,
Karl
November 10, 2009 at 6:08 pm |
I appreciate the lead and I have now successfully converted it to C#.
One last question – can you show the modifications needed to use databinding based on a unique user ID. Suppose I have 2 users, unique IDs and I want to show two different menu bars. I already have the DB side of this, I would like some more color on the databinding portion where the menu “refreshes” based on a new user ID.
November 10, 2009 at 8:45 pm |
Jim,
Best way is to select data from the database based on the user, then only bind that selected users data to the menu. In that case, the data will be dynamic as opposed to static.
Cheers,
Karl