带有子菜单的可选菜单项

8
你能在WPF中将顶级设置为可选,并带有子菜单吗?我似乎无法做到这一点。
<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>

顶级菜单项可以被选中,但子菜单项不会显示出来。有什么想法吗?

3个回答

6

如果您深入研究MenuItemControlTemplate,您会发现它根据其Role属性使用不同的模板。

参考资料:

菜单样式和模板

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

看起来它可以默认允许检查或子项。

为了解决这个问题,使用以下代码:

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>

代码后端:

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

我强烈建议将这个功能转换/封装成一个附加属性或者行为


那个方案可以用,除了我有实现IsChecked功能的子菜单,会导致级联效应。不过那是另一个问题了。 - kevindaub
这很接近了,但我需要关闭菜单,即表现得像这是实际选择的操作。相反,它仍然坐在那里等待输入... - Brady Moritz

0
补充decyclone的回答:
由于菜单在执行此操作后仍然保持打开状态,如果您想要关闭它,可以通过在父上下文菜单上设置IsOpen = false来关闭菜单:
private void MenuItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
    (sender as MenuItem).IsChecked = !(sender as MenuItem).IsChecked; 
    ((sender as MenuItem).Parent as ContextMenu).IsOpen = false;
} 

这会在你有多个子菜单时发挥作用。然后你需要遍历多个父级。 - kevindaub
在我的情况下,我只需要将其放在顶部菜单中。 - Brady Moritz

0

另一种方法是将 CheckBox 直接作为 MenuItem 的图标:

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

在这种情况下,用户必须点击复选框 - 而不是菜单项的任何位置 - 才能更改状态,而在其他地方点击将保持立即打开子菜单的默认行为(可能是或可能不是所需的)。此外,这允许三态值。特别是,如果顶级菜单应该作为所有子菜单的主开关,则空状态表示某些子状态已选中,而某些子状态未选中,则这非常好。至于decyclone的答案,除非采取进一步措施,否则菜单将保持打开状态。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接