Thursday, May 5, 2011

Binding data from the ViewModel to the Wpf Menu control

Databinding the Menu Control in wpf to a viewmodel can require a little bit of effort. As a menu control can contain hierarchical data, you must think of a data model to represent your menu items upfront, so I've decided to write a little about it. But before we dive into the meat and potatoes, lets try the basics first.

Lets see what a simple statically defined menu control looks like and then we'll transform this into a data model we can bind to from our viewmodel.

The statically defined menu :
<Grid>
    <Border VerticalAlignment="Top" Height="25" BorderThickness="0,0,0,1" 
            Background="#FFE4E2E2" BorderBrush="#FFA0A0A0">
        <Menu Height="25" VerticalAlignment="top">
            <MenuItem Header="_File">
                <MenuItem Header="New/Open project"/>
                <MenuItem Header="Save"/>
                <MenuItem Header="Exit"/>
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Header="About us"/>
            </MenuItem>
        </Menu>
    </Border>
</Grid>

When you run the above piece of code, this is what you will see:


Notice that each MenuItem is a HeaderedItemsControl. This is simply an ItemsControl with a Header.

Because each MenuItem is a HeaderedItemsControl, this allows our Menu to have an unlimited number of nesting as each Item has a header ( a label, the title you see displayed in the menu) and then an items collection (the sub menu items). If we wanted to see a qiuck example, this can be easily represented declaratively as follows :

<Menu>
      <MenuItem Header="_File">
           <MenuItem Header="New/Open project"/>
...
See how each MenuItem can in turn contain nested MenuItems, and a Header property is set to the title we want to display.

To bind the Menu control from our viewmodel, we need to be able to set the bindings for each menu items header and additionally we'll need to set a command argument so that we can identify the item clicked.

In order to achieve this we'll have to set bindings through the ItemContainerStyle. This will allow us to target the element generated for each MenuItem so that we can pass the Header value and additionally a command argument value to identify the item. By using a style element in ItemContainerStyle, we can set the TargetType to MenuItem as in the example code below, and the property setters will apply the value bindings to each MenuItem. Perfect.

<Menu ItemsSource="{Binding MenuItems}">
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Command" Value="{Binding Command}" />
            <Setter Property="CommandParameter" 
                       Value="{Binding CommandParameter}" />
            <Setter Property="Header" Value="{Binding Header}" />
            <Setter Property="ItemsSource" Value="{Binding Items}"/>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

Let's quickly walk through the piece of xaml above. As you can note, the Menu control itself is bound to a MenuItems collection exposed in our viewmodel.

Xaml :
<Menu ItemsSource="{Binding MenuItems}">

ViewModel:
public ObservableCollection<FileMenu> MenuItems
{
 get
 {
  return (_menuItems = _menuItems ??
   new ObservableCollection<FileMenu>());
 }
}

Xaml :
<Style TargetType="MenuItem">
            <Setter Property="Command" Value="{Binding Command}" />
            <Setter Property="CommandParameter" 
                       Value="{Binding CommandParameter}" />
            <Setter Property="Header" Value="{Binding Header}" />
            <Setter Property="ItemsSource" Value="{Binding Items}"/>
        </Style>

The above xaml binds to each item in the MenuItems ObservableCollection our viewmodel exposes.

Model:
public class FileMenu : ModelBase
{
        ...
 public string Header { get; set; }

 public string CommandParameter { get; set; }

 public ICommand Command
 {
  get
  {
   return (_command = _command ??
    new DelegateCommand<string>(
     OnMenuItemClick, (x)=> IsEnabled));
  }
 }
...
 public ObservableCollection<FileMenu> Items
 {
  get
  {
   return (_items = _items ??
    new ObservableCollection<FileMenu>());
  }
 }
 ...
}

The FileMenu class above represents each item in the MenuItem's collection. As you can see, it's a very simple object with just the needed properties. Note also that each item can also contain child items, this allows us to build a hierarchical menu dynamically in code. If we go back to look at the previous xaml listing above, this is exactly what the following line does :

<Setter Property="ItemsSource" Value="{Binding Items}"/>

The ItemsSource property is exposed by the MenuItem control. In this manner, if each FileMenu Item contains submenu items in turn, they will be bound. Nice!

We now have a perfectly bindable hierarchical menu and what more, it follows the MVVM pattern with clear separation.

Be sure to check out the sample application since code speaks for itself!

Download the sample code

3 comments:

  1. Found best multi event command binder in codeplex

    Multi Event Command Binder

    Benefits:

    1:Ability to bind multiple event to multiple commands.
    2:In CommandArgs in command handler you can even get original event args this was not possible with existing command binders.
    3:In CommandArgs in command handler you can even get original source of the event this was not possible with existing command binders.
    4:In this you can control individual CanExecute methods.

    Blogs
    Multi Event Command BlogSpot
    Multi Event Command WordPress

    ReplyDelete
  2. I added Icon in your sample but it is appearing with one MenuIttem only. I also changed Background color of MenuItem and color of all menu items is changed. In style I made following changes: Can you sugegst how can I change icon with bing or oen icon for all MenuItems.







    ReplyDelete