Jose
Jose

Reputation: 11091

WPF MenuItem template for MVVM

I am having trouble write the xaml representation to allow to bind to my background ViewModel for cascading menus

here's the VM:

public class MenuNode
{
  public string Header {get;}
  public List<MenuNode> Items {get;}
}

the xaml i have is this:

<ContextMenu ItemsSource="{Binding Choices}" >
    <ContextMenu.Resources>
        <DataTemplate DataType="{x:Type vmi:MenuNode}">
            <MenuItem Header="{Binding Header}" ItemsSource="{Binding Items}"/>
        </DataTemplate> 
    </ContextMenu.Resources>
</ContextMenu>

When the menu pops up I get the first-level entries with an arrow(indicating that there should be a sub-menu) but when i hover over the menu it doesn't show the sub-menu items.

Any ideas?

Upvotes: 4

Views: 5158

Answers (3)

Jan Van Overbeke
Jan Van Overbeke

Reputation: 280

my take on MenuItems and MVVM (it was not easy to put Icons in there)

  public class MenuItemVM
  {
    public string Text { get; set; }
    public List<MenuItemVM> Children { get; set; }
    public ICommand Command { get; set; }
    public ImageSource Icon { get; set; }
  }

public IList<MenuItemVM> AddContextMenu { get; set; }

View:

<Image x:Key="MenuItemIcon"
       x:Shared="false"
       Source="{Binding Icon}"
       Height="16px"
       Width="16px"/>

<Style x:Key="ContextMenuItemStyle" TargetType="{x:Type MenuItem}">
  <Setter Property="MenuItem.Icon" Value="{StaticResource MenuItemIcon}"/>
  <Setter Property="MenuItem.Command" Value="{Binding Command}" />
  <Style.Triggers>
    <!-- insert a null in ItemsSource if you want a separator -->
    <DataTrigger Binding="{Binding}" Value="{x:Null}">
      <Setter Property="Template" >
        <Setter.Value>
          <ControlTemplate>
            <Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}"/>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </DataTrigger>
  </Style.Triggers>
</Style>

Add the contextmenu to your UI

<ContextMenu ItemContainerStyle="{StaticResource ContextMenuItemStyle}"
             ItemsSource="{Binding AddContextMenu}">
  <ContextMenu.Resources>
    <HierarchicalDataTemplate DataType="{x:Type vmp:MenuItemVM}"
                              ItemsSource="{Binding Children}">
      <TextBlock Text="{Binding Text}"/>
    </HierarchicalDataTemplate>
  </ContextMenu.Resources>
</ContextMenu>

Upvotes: 4

Josh G
Josh G

Reputation: 14256

OK, here's the issue:

For some reason, the MenuItems that were generated by your DataTemplate are getting wrapped inside of another MenuItem (the result was nested MenuItems). The sub items were not being opened because the outer MenuItem had no children.

The solution is to use a HierarchicalDataTemplate instead:

<ContextMenu ItemsSource="{Binding Choices}" >
    <ContextMenu.Resources>
        <HierarchicalDataTemplate DataType="{x:Type vmi:MenuNode}" ItemsSource="{Binding Items}">
            <TextBlock Text="{Binding Header}"/>
        </HierarchicalDataTemplate> 
    </ContextMenu.Resources>
</ContextMenu>

Upvotes: 5

Josh G
Josh G

Reputation: 14256

Try using ObservableCollection<MenuNode> instead of List<MenuNode> if you are adding the nodes to the list after the binding is initialized the items collection will not get updated unless you are firing INotifyCollectionChanged (ObservableCollection does this).

Upvotes: 1

Related Questions