Search code examples
wpfmenuitem

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?


Solution

  • 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.