Three Ways To Create Dynamic Menus

There are quite a few sources about how to create dynamic menus (menus that are not embedded in XAML or code, but rather loaded from an external file):

  1. Loose XAML - Using the XamlReader, one can load an external, non-pre-compiled fragment of XAML, much like the one presented here:
    <MenuItem
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Header="_Simple">
    <MenuItem Header="1" />
    <MenuItem Header="2" />
    <MenuItem Header="3" />
    </
    MenuItem>
    The above fragment will then be loaded into the menu like so:
    using (FileStream looseXamlFile = File.OpenRead("Loose.xaml"))
    {
    this.mainMenu.Items.Insert(0,
    (MenuItem)XamlReader.Load(looseXamlFile));
    }
    And on the window, it will look like this:

    simple

    This is a very useful thing and you can read more about this method on Tamir's weblog.
    However, this way, you are bound to the elements of the WPF schema and all errors that would have occurred in the past during compile-time, will now occur during run-time. These presents a hardship for the developer. The bigger problem is what happens when receiving the items for the menu from a different source, such as a list you wish to bind to, in which case, you simply can't use Loose XAML.
  2. Data Templates - This way is the most intuitive way to bind a list of items or, better yet, an unary tree of items to a GUI representation of them. For instance, we have a list of Item objects (a simple class with one property - Value) and wish to bind them to menu items.
    <MenuItem x:Name="dataTemplateMenu" Header="_Data Template" >
    <MenuItem.Resources>
    <DataTemplate DataType="{x:Type local:Item}">
    <MenuItem Header="{Binding Path=Value}" />
    </DataTemplate>
    </MenuItem.Resources>
    </
    MenuItem>
    The XAML described here takes each Item and represents it using a MenuItem. To load a list into the menu, we simply call:
    dataTemplateMenu.ItemsSource = itemArray;
    After this call, all of the items will have MenuItems created for them and will look like this:

    data.templates 

    Very nice, but something is wrong here. Yes, the items are extremely large. Hovering over one reveals that we have something inside that menu:

    data.templates.hover

    Hum. That looks familiar. It's the same size as a normal MenuItem... Oh, wait, that's because it really is one. This is because when you create a Data Template, the items that enter the tree are actually your user-defined objects (in this case, the Item class). Before applying the template, the menu item asks the object about to be inserted into it whether it can be visually displayed. Because Item has no visual representation, the process creates a MenuItem to hold it, thus creating two menu items (the one automatically created and the one created using the Data Template).
  3. Item Container Style - So after two ways that didn't work well for us, the one that will is the MenuItem's ItemContainerStyle. This way, all you do is simply define a style for all of the menu items that will be placed under a certain menu item and use binding to bind those items to the values of your objects' properties:
    <MenuItem x:Name="itemContainerStyleMenu" Header="_Item Container Style">
    <MenuItem.ItemContainerStyle>
    <Style TargetType="{x:Type MenuItem}">
    <Setter Property="Header" Value="{Binding Path=Value}" />
    </Style>
    </MenuItem.ItemContainerStyle>
    </
    MenuItem>

    Then you set the source:
    itemContainerStyleMenu.ItemsSource = itemArray;
    and presto:

    item.container.style

    We now have what we needed.

    Extra: Recursive hierarchy could also be achieved using:
    1. Placing the style in the resources of a window, the application, etc. with a key.
    2. A setter of ItemsSource that binds to the collection of child items.
    3. A setter of ItemContainerStyle that will point to the style's key.

The source for this article is available here.

Pet peeve: Why would you change the "&Name" mnemonic notation from Win32 to something that is not better ("_Name" in WPF), only different? I'd have gone with a MenuItem.Mnemonic property that receives a character and doesn't pass validation if the Text property doesn't contain that character. If you're going to change, you might as well change for a reason.

8 Comments

Comments have been disabled for this content.