... a journey through WPF, MVVM and .NET4 RSS 2.0
# Tuesday, April 21, 2009

One question I've fielded a couple of times is how to manage menus, primarily context menus, with the MVVM pattern.  It turns out to be pretty easy once you know the "trick".  The key thing to keep in mind is that menus are just ItemsControls - they support data templating and binding like any other ItemsControl.  However, the part where people get lost is in hooking up the commands.  Here's the trick:

Step 1: Define some code behind construct to manage each menu item in a hiearchial fashion.  Here's one I've used:

public class MenuItem
{
   public string Text { get; set; }
   public List<MenuItem> Children { get; private set; }
   public ICommand Command { get; set; }
  
   public MenuItem(string item)
   {
       Text = item;
       Children = new List<MenuItem>();
   }
}

Step 2: Create your menu structure using the above container.

This involves just creating a List<MenuItem> which holds the root nodes and using a DelegatingCommand to wire up some code behind method.  Here's an example:

public List<MenuItem> MenuOptions
{
    get  {

       var menu = new List<MenuItem>();
       if (SupportedFileFormats.Count > 0)
      {
          var mi = new MenuItem("O_pen");
          foreach(var fl in SupportedFileFormats)
          {
               var sff = fl;
               mi.Children.Add(new MenuItem(fl.Attributes.Description) 
                      { Command = new DelegatingCommand(() => { LoadFromFormat(sff); })});
          }
          menu.Add(mi);
       }

       menu.Add(new MenuItem("Close _All") { Command = new DelegatingCommand(OnCloseAll, () => FileList.Count > 0)});
       return menu;
    }

}

Note the use of an anonymous method to suck the correct file format into the command handler.  A nice trick so the command is executed with some contextual information of what you clicked on.

Finally, you need to setup the context menu style:

<Style x:Key="ContextMenuItemStyle">
  
<Setter Property="MenuItem.Header" Value="{Binding Text
}"/>
   
<Setter Property="MenuItem.ItemsSource" Value="{Binding Children
}"/>
   
<Setter Property="MenuItem.Command" Value="{Binding Command
}" />
</Style>

And then use the style where you want the menu to appear:

<StackPanel Orientation="Horizontal">
   
<Image Source="{Binding Image}" Width="16" Height="16" />
   
<TextBlock Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding Header}" />
   
<StackPanel.ContextMenu>
      
<ContextMenu ItemContainerStyle="{StaticResource ContextMenuItemStyle}" ItemsSource="{Binding MenuOptions}" />
   
</StackPanel.ContextMenu>
</StackPanel>

</HierarchicalDataTemplate>

Now the context menu is populated and properly dispatches to your ViewModel commands!

Edit: added intermediate object in LINQ query to fix deferred query issue pointed out in comments.
 

Tuesday, April 21, 2009 5:35:59 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
.NET | WPF
Comments are closed.
I'm a WPF Disciple
Search
Categories
Archive
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Mark Smith
Sign In
All Content © 2010, Mark Smith
DasBlog theme 'Business' created by Christoph De Baene (delarou)