Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 562
Jose Avatar asked Dec 28 '22 22:12

Jose


2 Answers

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>
like image 57
Josh G Avatar answered Dec 30 '22 12:12

Josh G


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>
like image 45
Jan Van Overbeke Avatar answered Dec 30 '22 11:12

Jan Van Overbeke