Cubi73
Cubi73

Reputation: 1951

Bind command to `DataTemplate` in `ContextMenu` when using multiple templates?

I'm trying to give a context menu items with different look and functionality, but I cannot find a way to bind commands to these items. Each menu item's view model is derived from one class AbstractEntryViewModel. Here is a shortened example of my current project's structure. Using ContextMenu.Resources is the only way I found to bind a template to a certain type.

<ContextMenu ItemsSource="{Binding Entries}">
    <ContextMenu.Resources>

        <DataTemplate DataType="{x:Type local:NopEntryViewModel}">
            <!-- Content -->
        </DataTemplate>

        <HierarchicalDataTemplate
            DataType="{x:Type local:GroupEntryViewModel}"
            ItemsSource="{Binding Entries}">
            <!-- Content -->
        </HierarchicalDataTemplate>

        <!-- More templates -->

    </ContextMenu.Resources>
</ContextMenu>
internal abstract AbstractEntryViewModel : INotifyPropertyChanged {
    public abstract void Invoke ();
    // ...
}

internal NopEntryViewModel : AbstractEntryViewModel {
    public override void Invoke () {}
}

internal GroupEntryViewModel : AbstractEntryViewModel {
    public override void Invoke () { /* ... */ }
    // ...
}

// More view models

Normally I can bind a command to a MenuItem like so

<MenuItem Command="{Binding StaticResourceOrViewModelProperty}" />

How can I do the same thing with data templates? Is there an invisible container, a wrapper for the data template's content, that I can use to bind a command to?

Upvotes: 1

Views: 476

Answers (1)

Massimiliano Kraus
Massimiliano Kraus

Reputation: 3833

For simplicity, suppose 2 derived ViewModels, VM1 and VM2, with respectively a Command Command1 and a Command2.

Two steps:

1) Define in the base ViewModel this property:

public Type Type
{
    get { return GetType(); }
}

We cannot use GetType() directly, we need a wrapping property since WPF Bindings work only with properties.

2) Use this property to set a DataTrigger on the Style for the ContextMenu MenuItem:

<ContextMenu.Resources>
  <Style TargetType="{x:Type MenuItem}">
    <Style.Triggers>
      <DataTrigger Binding="{Binding Type}" Value="{x:Type local:VM1}">
        <Setter Property="Command" Value="{Binding Command1}"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding Type}" Value="{x:Type local:VM2}">
        <Setter Property="Command" Value="{Binding Command2}"/>
      </DataTrigger>
      <!-- add other DataTriggers, one for every ViewModel --> 
    </Style.Triggers>
  </Style>
</ContextMenu.Resources>

The DataTemplates are set as you do, with:

<DataTemplate DataType="{x:Type local:VM1}">
  <!-- Content -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:VM2}">
  <!-- Content -->
</DataTemplate>

Upvotes: 2

Related Questions