Workarounds for supporting MVVM in the Silverlight ContextMenu service

As I discussed in my last post, some of the Silverlight controls does not support MVVM quite well out of the box without specific customizations. The Context Menu is another control that requires customizations for enabling data binding on the menu options. There are a few things that you might want to expose as view model for a menu item, such as the Text, the associated icon or the command that needs to be executed. That view model should look like this,

public class MenuItemModel
{
    public string Name { get; set; }
    public ICommand Command { get; set; }
    public Image Icon { get; set; }
    public object CommandParameter { get; set; }
}

This is how you can modify the built-in control to support data binding on the model above,

public class CustomContextMenu : ContextMenu
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        CustomMenuItem item = new CustomMenuItem();
        
        Binding commandBinding = new Binding("Command");
        item.SetBinding(CustomMenuItem.CommandProperty, commandBinding);
 
        Binding commandParameter = new Binding("CommandParameter");
        item.SetBinding(CustomMenuItem.CommandParameterProperty, commandParameter);
 
        return item;
    }
}
 
public class CustomMenuItem : MenuItem
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        CustomMenuItem item = new CustomMenuItem();
 
        Binding commandBinding = new Binding("Command");
        item.SetBinding(CustomMenuItem.CommandProperty, commandBinding);
 
        return item;
    }
}
The change is very similar to the one I made in the TreeView for manually data binding some of the Menu item properties to the model.

Once you applied that change in the control, you can define it in your XAML like this.

<toolkit:ContextMenuService.ContextMenu>
    <e:CustomContextMenu ItemsSource="{Binding MenuItems}">
        <e:CustomContextMenu.ItemTemplate>
           <DataTemplate>
               <StackPanel Orientation="Horizontal"  >
                   <ContentPresenter Margin="0 0 4 0" Content="{Binding Icon}" />
                   <TextBlock Margin="0" Text="{Binding Name, Mode=OneWay}" FontSize="12"/>
               </StackPanel>
           </DataTemplate>
        </e:CustomContextMenu.ItemTemplate>
    </e:CustomContextMenu>
</toolkit:ContextMenuService.ContextMenu>

The property MenuItems associated to the “ItemsSource” in the parent model just returns a list of supported options (menu items) in the context menu.

this.menuItems = new MenuItemModel[]
{
    new MenuItemModel
    {
        Name = "My Command",
        Command = new RelayCommand(OnCommandClick),
        Icon = ImageLoader.GetIcon("command.png")
    }
};

The only problem I found so far with this approach is that the context menu service does not support a HierarchicalDataTemplate in case you want to have an hierarchy in the context menu (MenuItem –> Sub menu items), but I guess we can live without that.

No Comments