Introduction
Recently I needed to auto generate context menu items bound to command collection in my View Model. and the data template needed to be different based on the type of the item in the list. Article is outcome of what I learned from it.
Background
From the title it sounds like a 30-second task where all you need is to have a collection with different command items and bind it to context menu's ItemsSoruce and use ItemTemplateSelector. I started with the same assumption but solution turned out to be a bit more trickier.
Using the code
Well solution is straight forward. All you need it to remember to use ItemContainerTemplateSelector
. But that is not all, you will also need to set property UsesItemContainerTemplate
in order for your template selector to be effective.
A very basic example to illustrate is attached:
In the example, ContextMenu
is bound to property ContextItems
<ContextMenu ItemsSource="{Binding Path=ContextItems}" UsesItemContainerTemplate="True" ItemContainerTemplateSelector="{StaticResource _templateSelector}" />
ContextItems
is collection of ICommandItem
which contains properties like Text
(header), ToolTip
and Command
.
ContextItems = new List<ICommandItem>(){
new SimpleCommandItem() { Text ="Simple Command 1", ToolTip="Simple Tooltip 1"},
new SeparatorCommandItem(),
new SimpleCommandItem() { Text ="Simple Command 2", ToolTip="Simple Tooltip 2"}
SimpleCommandItem class is implementation for the interface.
SeparatorCommandItem
implements the interface explicitly but throws exceptions on implementation (for obvious reasons!).
ContextMenuResources.xaml is resource dictionary that holds data templates for both the classes.
SimpleCommandItem Template:
<DataTemplate x:Key="SimpleCommandItem" DataType="local:SimpleCommandItem">
<MenuItem Header="{Binding Path=Text}" ToolTip="{Binding Path=ToolTip}" Command="{Binding Command}" />
</DataTemplate>
And the separator Template:
<DataTemplate x:Key="SeparatorCommandItem" DataType="local:SeparatorCommandItem">
<Separator />
</DataTemplate>
All that is left to do is to implement ContextMenuItemContainerTemplateSelector
public class ContextMenuItemContainerTemplateSelector : ItemContainerTemplateSelector
{
private static ResourceDictionary _dictionary;
static ContextMenuItemContainerTemplateSelector()
{
_dictionary = new ResourceDictionary();
_dictionary.Source = new Uri(@"pack://application:,,,/ContextMenu.Framework;component/ContextMenuResources.xaml");
}
public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl)
{
var name = item == null ? null : item.GetType().Name;
if (name != null && _dictionary.Contains(name))
{
return (DataTemplate)_dictionary[name];
}
return null;
}
}
History
Keep a running update of any changes or improvements you've made here.