Creating dynamic menus in WPF using the HierarchicalDataTemplate

| | Comments (0) | TrackBacks (0)
This is really not a programming blog.  (It's scarcely a blog at all; I see that I haven't made a posting in about 11 months.)  But in the last couple of days I've run across some pretty bad misinformation about how to use the HierarchicalDataTemplate to create menus in WPF, misinformation that makes it seem much, much harder than it actually is.  The problem's exacerbated by the fact that Microsoft's documentation on the HierarchicalDataTemplate is of the gosh-it-magically-works variety, which doesn't really help you if you're trying to troubleshoot problems.

It's actually much easier to do this than a lot of people seem to think - at least, "easy" in WPF terms, which is to say, "utterly baffling until you begin identifying with your torturers."

Here's the story:

Just as a DataTemplate is a template for generating content controls, a HierarchicalDataTemplate is a template for generating headered controls.  

A headered control has two key properties:  Header and Items.  The Header property is a content control, and the Items property is an items control.  When a HierarchicalDataTemplate is applied to an object, it creates a headered control, uses the template to populate the control's Header, and then populates the control's Items by applying the HierarchicalDataTemplate to the child objects that have been specified by binding to the template's ItemsSource.

A trivial HierarchicalDataTemplate looks like this:

    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <TextBlock Text="{Binding Text}"/>
    </HierarchicalDataTemplate>

This creates a headered control, sets its header to a TextBlock containing the source item's Text property, and then populates its Items by applying a template to each object in the source item's Children property.

The first time you start to work with this, you'll probably think to yourself, "Well, I want each item that gets created by this template to be a MenuItem," and make a template that maybe looks something like this:

    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <MenuItem Header="{Binding Text}" Command="{Binding Command}"/>
    </HierarchicalDataTemplate>

You are now in a world of hurt.  

When you use a template to create items in a ListBox, you don't put a ListBoxItem in the data template, because you know (or should know, at least), that the ListBox generates the item container for you.  That is, this:

    <ListBox ItemsSource="{Binding Items}"/>

is going to generate a ListBoxItem for every object in the Items collection, and then use whatever data template it can find to produce the ListBoxItems' content. 

Well, so the Menu does this too.  A Menu like this:

    <Menu ItemsSource="{Binding Items}"/>

will create a MenuItem for every object in the Items collection.  And it will create a MenuItem for each of the child items in the HierarchicalDataTemplate's ItemsSource.  And so on down the hierarchy.

So if you put a MenuItem in your template, like I did in the seeminly simple example above, what will be created is an invisible MenuItem that has no header, and no command - but that does contain a MenuItem.  It will behave oddly, to say the least.

Well, okay, but then how do you set the command binding on the menu item?  Here is where things start to break down in the other examples you can find online.  If you look at this example, by the normally reliable Karl Shiflett, you'll find all of this code-behind that hooks into the content's Loaded event and navigates the visual tree to find the MenuItem that just got created and set its Command to the DataContext's Command property.  You do not need to do this.  You can do everything you need to do in XAML.

Just as you would in an ordinary data template that produces a ListBoxItem, you can set properties on the MenuItem that the HierarchicalDataTemplate produces, by using the ItemContainerStyle:

    <HierarchicalDataTemplate.ItemContainerStyle>
      <Style TargetType="MenuItem">
        <Setter Property="Command" Value="{Binding Command}"/>
      </Style>
    </HierarchicalDataTemplate.ItemContainerStyle>

That style gets applied to every MenuItem that the template generates and sets its Command correctly.  Now everything just works.

Another common issue when creating dynamic menus is creating item separators.  Menus created manually (i.e. in XAML) can contain a heterogenous mix of MenuItem and Separator objects, and look right.  But if you're using a HierarchicalDataTemplate, every item created is a MenuItem.  And if you simply make that MenuItem contain a Separator object, it looks wrong.

What you have to do instead is swap out the control template for those menu items.  This is easy to do:  just set a DataTrigger in the ItemContainerStyle that sets the Template property if the source object's IsSeparator (or whatever) property is set.

This is already getting pretty long-winded, and so i'll just point you to the fully-worked example project I built, which shows how to create fairly elaborate hierarchical menus entirely via data binding.

0 TrackBacks

Listed below are links to blogs that reference this entry: Creating dynamic menus in WPF using the HierarchicalDataTemplate.

TrackBack URL for this entry: http://www.koaxkoaxkoax.com/cgi-sys/cgiwrap/uhhhclem/managed-mt/mt-tb.cgi/22

Leave a comment

About this Entry

This page contains a single entry by Bob Rossney published on September 18, 2010 9:13 PM.

Incident was the previous entry in this blog.

Why doesn't it feel right? We told you it's right. What's wrong with you? is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Powered by Movable Type 4.34-en