绑定静态ICommand到菜单项而无需ViewModel

3

我想要将命令绑定到不同的MenuItems上,在这个ContextMenu中,我将会与一个Button连接起来。但是,为了实现这一点,我需要在我导入的ResourceDictionary中定义所有的命令为static

public class DesignerCanvas{
    ....
    public static RoutedCommand MyCommand = new RoutedCommand();
    ....
}

在我的 MainWindow.xaml 中,我将这个命令与我的 MainWindow.xaml.cs 中的实现挂钩,如下:

<CommandBinding Command="{x:Static Designer:DesignerCanvas.MyCommand}"
                    Executed="DoStuff"
                    CanExecute="CanDoStuff" /> 

在我的ResourceDictionary.xaml中,我有一个Button,我使用TriggersContextMenu与之连接:

    <Button x:Name="btnMyButton" Content="Click this">
      <Button.Style>
        <Style TargetType="{x:Type Button}">
          <Style.Triggers>
            <EventTrigger RoutedEvent="Click">
              <EventTrigger.Actions>
                <BeginStoryboard>
                  <Storyboard>
                    <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
                      <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
                    </BooleanAnimationUsingKeyFrames>
                  </Storyboard>
                </BeginStoryboard>
              </EventTrigger.Actions>
            </EventTrigger>
          </Style.Triggers>
          <Setter Property="ContextMenu">
            <Setter.Value>
              <ContextMenu>
                <MenuItem x:Name="myMenu" Header="MyMenuItem 1">
                  <MenuItem x:Name="menuItem1" Header="MySubMenuItem 1"
Command="{x:Static DesignerItems:DesignerCanvas.MyCommand}">     <<<=== Command Binding
                    <MenuItem.Icon>
                      <Image Source="myImage.png" Width="20"/>
                    </MenuItem.Icon>
                  </MenuItem>
                </MenuItem>
              </ContextMenu>
            </Setter.Value>
          </Setter>
        </Style>
      </Button.Style>
    </Button>

但这似乎不起作用,因为在XAML中指定了命令的菜单项被显示为禁用,并且调试器也未触发CanDoStuff()DoStuff()。另外,由于我没有使用ViewModel,所以我无法编写以下内容:

<MenuItem Command="{Binding Path=somePathInViewModel, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" />

我该如何做到这一点,任何帮助都将不胜感激。提前感谢您的帮助。

1
我相信你无法在没有视图模型的情况下做到你想要的事情,或者看起来是这样的。如果你不喜欢 VM,你可以定义一个静态/单例类来公开你的 ICommands。 - Mario Vernari
@MarioVernari 我也尝试过这种方式,即使用具有静态方法实现的 static 类,但仍然没有成功。 - Agent007
你不想使用视图模型的原因是什么?输出窗口中是否显示绑定错误? - J King
@JKing 1. 我并没有特别的原因去采用这种方法。我为每个表单都有一个ViewModel,但我不想为菜单项单独创建一个ViewModel,那样会使事情变得非常复杂。2. 不,没有绑定错误。 - Agent007
2个回答

4

终于让它正常工作了,之前犯了一些低级错误!

我必须将static命令初始化更改为以ContextMenu为其所有者。

public static RoutedCommand MyCommand = new RoutedCommand("MyCommand", typeof(ContextMenu));

MainWindow.xaml.cs中,添加了一个方法来注册命令绑定,并从MainWindow的构造函数中调用它:
private void InitializeMenuItemsCommands()
    {
        CommandManager.RegisterClassCommandBinding(typeof(ContextMenu), new CommandBinding(DesignerCanvas.TestDialog, OpenTestDialog, CanOpenTestDialog));
    }

最后是CanExecuteExecuted事件的处理程序:

private void CanOpenTestDialog(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true; // set if MenuItem is enabled
}

private void OpenTestDialog(object sender, ExecutedRoutedEventArgs e)
{
    // handle the MenuItem click here
}

我不需要在我的ResourceDictionary中更改任何内容来处理MenuItem,因此这里再次呈现该MenuItem

<MenuItem x:Name="menuItem1" Header="MySubMenuItem 1"
Command="{x:Static DesignerItems:DesignerCanvas.MyCommand}">     <<<=== Command Binding
                <MenuItem.Icon>
                  <Image Source="myImage.png" Width="20"/>
                </MenuItem.Icon>
              </MenuItem>

好的。很高兴这个可以工作,尽管我认为你想要留在XAML中。 - dev hedgehog
你的回答在我苦思冥想了好几个小时后真的帮了我大忙。给你点赞! - user1618054

3
您在代码中做得很好,但是关于WPF中的ContextMenu有一些隐藏的错误/特性。这些淘气鬼缺乏WPF通常的功能。实际上,ContextMenu只是WPF中每个控件可用的一个属性,它会在用户右键单击时将其项目列表注入VisualTree。
为解决您的问题,请勿在样式中设置ContextMenu,也不要在窗口级别上设置CommandBinding,而是在ContextMenu级别上设置。
最重要的是不要在样式中设置ContextMenu!
编辑:
将ContextMenu放在样式之外,并像这样设置您的CommandBinding:
<ContextMenu>
    <ContextMenu.CommandBindings>
        <CommandBinding Command="foo:MyCommands.CmdFoo" 
                        CanExecute="CanExecuteRerollCommand" 
                        Executed="ExecuteRerollCommand" />
    </ContextMenu.CommandBindings>
    <MenuItem Header="Reroll"  Command="foo:MyCommands.CmdFoo"/>
</ContextMenu>

谢谢您的回答。有什么指导我该如何做吗?那将非常有帮助。 - Agent007
指针已经包含在答案中了。仔细看一下 :) - dev hedgehog
不,我的意思是在这个方向上有任何示例,我对WPF还很陌生。 :) - Agent007
好的,谢谢,我会尝试的! :) - Agent007
谢谢,我试过了,但是由于我在使用ResourceDictionary,无法使其正常工作。除了将此移动到MainWindow.xaml之外,您有什么建议吗? - Agent007

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