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.




February 3, 2008 at 6:14 pm |
[...] post by Karl On WPF – .Net Share and Enjoy: These icons link to social bookmarking sites where readers can share and [...]
February 11, 2008 at 8:01 pm |
Hi Karl, its possible to use a XmlDocument as the dynamic source for the menu items? I found this sample in the msdn forums (http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2827907&SiteID=1) but there is a limit about the numbers of child elements. Any samples or reference would be appreciated. Regards Rodrigo
February 11, 2008 at 8:09 pm |
Don,
Yes, you can use and XML file as your source. The very cool feature of the HierarchicalDataTemplate is that is has no limit to the number of levels.
This solution takes a flat structure and expands it to menu levels.
If, your XML file already has the levels defined, I know that you “can” use it, I just have not done this yet.
Here is a post that I “think” does what you want. http://blogs.msdn.com/llobo/rss.xml
I hope this is some help. Have a great day!
Cheers,
Karl
February 12, 2008 at 6:52 pm |
Here is another example I just found:
http://blogs.msdn.com/wpfsdk/archive/2008/01/29/a-sample-and-a-question.aspx
Karl
February 24, 2008 at 1:30 pm |
[...] Part 2 – Form Notification Control That Binds To IDataErrorInfo.Error PropertyKarl Shifflett – WPF Sample Series – Databound HierarchicalDataTemplate Menu SamplePete Brown – Silverlight, WPF, Windows Forms, Ajax – Which One is for Me? Technorati tags: Smart [...]
April 9, 2008 at 10:00 pm |
Hi Karl
I have a question about MVC, I thought that I could use this post and turn it into a MVC application, I created two additional projects, Model & Controller. In the Model project I moved the MenuData items and the Helpers. Into the Controller I was going to move the Converters folder, however the MenuDataItemConverter has a reference to ApplicationMenuWindow. I have been studying Josh’s posting about MVC & WPF it seems that this reference wouldn’t be a good thing to have in a MVC style app.
Do you have any suggestions as how best to make changes to avoid this ?
Would I be better off just leaving the converter as part of the View ?
Thanks in advance for any advise
April 9, 2008 at 10:11 pm |
The windows is intended to be shell for other user controls that are loaded by the menu.
I would implement MVC for the user controls (those are the forms, “User Maintenance” “Group Maintenance” etc.
When I write my applications, I code the shell as is and leave the MVC for the application forms, but that’s just me. This shell has a model, but the control code is in the code behind for the window for the exact reasons you have mentioned.
I hope this helps!
Cheers,
Karl
November 25, 2008 at 8:54 pm |
[...] apparent to me, though it’s actually quite simple. A while back Karl Shifflett wrote up an excellent example of doing something similar. I take his example one step further by showing how you can easily [...]
December 3, 2008 at 4:59 am |
Instead of using the ItemContainerStyleSelector=”{StaticResource menuSeparatorStyleSelector}”, wouldn’t it be better to define a data trigger on IsSeparator property like
<ContextMenu x:Key=”MyContextMenu” ItemsSource=”{Binding MenuOptions}”>
<ContextMenu.ItemContainerStyle>
<Style TargetType=”{x:Type MenuItem}”>
<Style.Triggers>
<DataTrigger Binding=”{Binding IsSeparator}” Value=”True”>
<Setter Property=”Template”>
<Setter.Value>
<ControlTemplate TargetType=”{x:Type MenuItem}”>
<Separator HorizontalAlignment=”Stretch” IsEnabled=”false”/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
&t;/Style >
</ContextMenu.ItemContainerStyle>
</extMenu>
December 6, 2008 at 9:04 am |
irfanamd,
This is why I love WPF. There are many ways to accomplished the same task.
I have not yet profiled the two solutions, do you think your solution would perform better?
Have a great day,
Karl
December 16, 2008 at 12:50 pm |
Love the example and would like to use this approach but I CAN’T see how to do the work. I don’t see anything executing when a user clicks on a menu item and I don’t know how to bind the menuitem to a method to execute. Please fill in that piece of the puzzle.
December 16, 2008 at 3:51 pm |
Nevermind….I figured it out.
December 18, 2008 at 3:30 pm |
Frank,
Glad you sorted it out.
Cheers,
Karl
May 11, 2009 at 8:24 pm |
Hi Karl. Very nice! I’m still wrapping my head around WPF–how would we get the child items if building a user control or if building this in a browser app? I can’t cast the container as a System.Windows.DependencyObject.
Thanks!
May 11, 2009 at 8:33 pm |
Sorry–it’s late at night and I’m not thinking straight. I mean this is the line I don’t know how to duplicate with a User Control or browser app:
Dim objWindow As ApplicationMainWindow = CType(App.Current.MainWindow, ApplicationMainWindow)