kevindaub
kevindaub

Reputation: 3363

Checkable MenuItem With Sub Menus

Can you have submenus with the top level set to checkable in WPF? I can't seem to get this to work.

<Window.ContextMenu>
    <ContextMenu>
        <MenuItem Header="Top Level 1" IsCheckable="True" IsChecked="True">
            <MenuItem Header="Sub Level" />
            <MenuItem Header="Sub Level" />
        </MenuItem>
        <MenuItem Header="Top Level 2">
            <MenuItem Header="Sub Level" />
            <MenuItem Header="Sub Level" />
        </MenuItem>
    </ContextMenu>
</Window.ContextMenu>

Top Level 1 is checkable, but the sub levels don't appear. Any thoughts?

Upvotes: 9

Views: 9746

Answers (3)

M Kloster
M Kloster

Reputation: 698

Another approach is to simply use a CheckBox as the MenuItem's Icon:

<MenuItem>
    <MenuItem.Icon>
        <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding MyCheckProperty}"/>
    </MenuItem.Icon>
    <MenuItem Header="Item1"/>
    <MenuItem Header="Item2"/>
</MenuItem>

In this case the user must click the CheckBox - not just anywhere on the MenuItem - in order to change the state, while clicking elsewhere will keep the default behavior of immediately opening the submenu (which may or may not be desired). Also, this allows for three-state values. In particular, this is excellent if the top level menu should function as a master switch for all its submenus, with the null state indicating that some substates are checked, and some are not. As for decyclone's answer, the menu will stay open unless further measures are taken.

Upvotes: 0

Brady Moritz
Brady Moritz

Reputation: 8903

To add to decyclone's answer:

Since the menu will sit there still open after doing this, and if you want it to close, you can then close the menu by setting IsOpen = false on the parent contextmenu:

private void MenuItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
    (sender as MenuItem).IsChecked = !(sender as MenuItem).IsChecked; 
    ((sender as MenuItem).Parent as ContextMenu).IsOpen = false;
} 

Upvotes: 0

decyclone
decyclone

Reputation: 30820

If you dig into MenuItem's ControlTemplate, you will see that it uses different templates depending on it's Role property.

Reference:

Menu Styles and Templates

<Style x:Key="{x:Type MenuItem}"
       TargetType="{x:Type MenuItem}">
  <Setter Property="OverridesDefaultStyle"
          Value="True" />
  <Style.Triggers>
    <Trigger Property="Role"
             Value="TopLevelHeader">
      <Setter Property="Template"
              Value="{StaticResource {x:Static MenuItem.TopLevelHeaderTemplateKey}}" />
      <Setter Property="Grid.IsSharedSizeScope"
              Value="true" />
    </Trigger>
    <Trigger Property="Role"
             Value="TopLevelItem">
      <Setter Property="Template"
              Value="{StaticResource {x:Static MenuItem.TopLevelItemTemplateKey}}" />
    </Trigger>
    <Trigger Property="Role"
             Value="SubmenuHeader">
      <Setter Property="Template"
              Value="{StaticResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}" />
    </Trigger>
    <Trigger Property="Role"
             Value="SubmenuItem">
      <Setter Property="Template"
              Value="{StaticResource {x:Static MenuItem.SubmenuItemTemplateKey}}" />
    </Trigger>
  </Style.Triggers>
</Style>

Seems like it can either allow checking or subitems by default.

To workaround that, use following code:

XAML:

<ContextMenu>
    <MenuItem Header="Top Level 1" 
              Mouse.PreviewMouseUp="MenuItem_MouseLeftButtonUp">
        <MenuItem Header="Sub Level" />
        <MenuItem Header="Sub Level" />
    </MenuItem>
    <MenuItem Header="Top Level 2">
        <MenuItem Header="Sub Level" />
        <MenuItem Header="Sub Level" />
    </MenuItem>
</ContextMenu>

Code behind:

private void MenuItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    (sender as MenuItem).IsChecked = !(sender as MenuItem).IsChecked;
}

I strongly recommend converting/encapsulating this piece of functionality into an Attached Property or a Behavior.

Upvotes: 6

Related Questions