如何在WPF中添加自定义路由命令?

51

我有一个包含菜单和子菜单的应用程序。 我已经对一些子菜单项目附加了应用程序命令,例如剪切、复制和粘贴。
还有一些其他没有应用程序命令的菜单项。
我如何将自定义命令绑定到这些子菜单项?
我已经阅读了这篇文章,但无法将事件附加到我的子菜单项。


1
发现了这个YouTube链接。http://www.youtube.com/watch?v=mG4l0AaYBTM - Sangram Nandkhile
3个回答

100

我使用一个静态类,放在Window1类(或者无论窗口类名是什么)之后,在该静态类中创建RoutedUICommand类的实例:

public static class Command {

    public static readonly RoutedUICommand DoSomething = new RoutedUICommand("Do something", "DoSomething", typeof(Window1));
    public static readonly RoutedUICommand SomeOtherAction = new RoutedUICommand("Some other action", "SomeOtherAction", typeof(Window1));
    public static readonly RoutedUICommand MoreDeeds = new RoutedUICommand("More deeds", "MoreDeeeds", typeof(Window1));

}

在窗口标记中添加一个命名空间,使用Window1类所在的命名空间:

xmlns:w="clr-namespace:NameSpaceOfTheApplication"

现在我可以像为应用程序命令一样为命令创建绑定:

<Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.Open" Executed="CommandBinding_Open" />
    <CommandBinding Command="ApplicationCommands.Paste" Executed="CommandBinding_Paste" />
    <CommandBinding Command="w:Command.DoSomething" Executed="CommandBinding_DoSomething" />
    <CommandBinding Command="w:Command.SomeOtherAction" Executed="CommandBinding_SomeOtherAction" />
    <CommandBinding Command="w:Command.MoreDeeds" Executed="CommandBinding_MoreDeeds" />
</Window.CommandBindings>

例如,在菜单中使用绑定:

<MenuItem Name="Menu_DoSomething" Header="Do Something" Command="w:Command.DoSomething" />

6
对我来说,拥有一个 static 类来保存 RoutedUICommands 是关键。如果它们在我的 Window1 类中,XAML 就找不到它们了。谢谢! - ewall
10
静态类的重要性不容小觑。令人沮丧的是,如果命令是在非静态类中定义的,WPF似乎会悄悄地忽略它们。 - Patrick Linskey
5
为什么要点踩?如果你不解释哪里有问题,那么回答就无法得到改进。 - Guffa
FYI - 不要犯我曾经犯过的初学者错误:静态属性(例如Command.DoSomething)必须是public,否则你会收到以下异常信息:“CommandConverter无法从System.String进行转换”。我猜测这是因为管理命令的代码可能在不同的程序集中,因此无法看到internal属性。我上周刚开始学习WPF :D - Pressacco
我谷歌了很多次,直到找到这个简洁明了的解释来完成任务。谢谢! - CodeMonkey
显示剩余2条评论

73

您可以直接在XAML中声明命令,而不是在静态类中定义它们。以下是一个示例(改编自Guffas的好例子):

<Window.Resources>
    <RoutedUICommand x:Key="DoSomethingCommand" Text="Do Something" />
    <RoutedUICommand x:Key="DoSomethingElseCommand" Text="Do Something Else" />
</Window.Resources>
<Window.CommandBindings>
    <CommandBinding Command="{StaticResource DoSomethingCommand}" Executed="CommandBinding_DoSomething" />
    <CommandBinding Command="{StaticResource DoSomethingElseCommand}" Executed="CommandBinding_DoSomethingElse" />
</Window.CommandBindings>
...
<MenuItem Name="Menu_DoSomething" Header="Do Something" Command="{StaticResource DoSomethingCommand}" />

1
这些内容是否与实际的C#代码绑定?如果是,如何绑定?RoutedUICommands有什么作用?它们添加了哪些功能? - 15ee8f99-57ff-4f92-890c-b56153
1
@EdJ.Plunkett:1. 是的。2. 通过CommandBindings:CommandBinding_DoSomethingCommandBinding_DoSomethingElse是C#代码。3./4. 关于RoutedCommands与常规Button_Click绑定的优势的一般讨论,请参见例如http://joshsmithonwpf.wordpress.com/2008/03/18/understanding-routed-commands/(第“谁关心?”部分)。 - Heinzi
谢谢。我想知道RoutedUICommands相对于Guffa的示例有什么增加,而不是相对于常规的旧式onclick处理(如果我理解你的话——如果我必须这样做,我就不会问这些问题!),但我会在Josh Smith的文章中寻找答案。 - 15ee8f99-57ff-4f92-890c-b56153
@EdJ.Plunkett:啊,好的。Guffa也使用RoutedUICommands,他只是在静态类中声明它们,而不是在XAML中。这是他的示例和我的唯一区别。这只是关于你想把它们放在哪里的偏好问题。 - Heinzi
2
我最喜欢这个答案。更加简洁。 - Eternal21

20

我知道我的答案来晚了,但我希望它能对未来有所帮助。

我喜欢Guffa和Heinzi的回答,但是你只需要使用一个命令就可以达到之前的结果。我通常使用Help命令。

 <Window.CommandBindings>
        <CommandBinding Command="{StaticResource Help}" Executed="HelpExecuted" />
  </Window.CommandBindings>

我每次调用都使用CommandParameter参数,例如:

<Window.InputBindings>
    <KeyBinding Command="{StaticResource Help}" Key="A" Modifiers="Ctrl" CommandParameter="Case1"/>
    <KeyBinding Command="{StaticResource Help}" Key="B" Modifiers="Ctrl" CommandParameter="Case2"/>
    <KeyBinding Command="{StaticResource Help}" Key="C" Modifiers="Ctrl" CommandParameter="Case3"/>
    <KeyBinding Command="{StaticResource Help}" Key="D" Modifiers="Ctrl" CommandParameter="Case4"/>
    <MouseBinding Command="{StaticResource Help}" MouseAction="LeftDoubleClick" CommandParameter="Case5" />
</Window.InputBindings>
或者
<Button Command="Help" CommandParameter="Case6" Content="Button">
    <Button.InputBindings>
        <KeyBinding Command="{StaticResource Help}" Gesture="Ctrl+D" CommandParameter="Case7"/>
    </Button.InputBindings>
</Button>

并且在cs文件中

private void HelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
    string str = e.Parameter as string;
    switch (str)
    {
        case null://F1 Pressed default Help
               //Code
            break;
        case "Case1":
               //Code
            break;
        case "Case2":
               //Code
            break;
        case "Case3":
               //Code
            break;
        case "Case4":
            break;
        case "Case5":
               //Code
            break;
        case "Case6":
               //Code
            break;
        case "Case7":
               //Code
            break;
    }
    e.Handled = true;
}

如果你正在使用 MVVM 模式

private void HelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
    string str = e.Parameter as string;
    Mvvm_Variable.Action(Input: str);
    e.Handled = true;
}

并将开关移动到ViewModule站点。Action是同一ViewModule类中的一个方法。


1
太棒了!使用Command的Properties值为我打开了许多新的可能性 :-) 我刚刚使用了ApplicationCommands.NotACommand,结合MinimizeWindow、MaximizeWindow和RestoreDownWindow的Properties。 - Riegardt Steyn
我喜欢这个(任何可以减少样板文件的东西!),但值得注意的是它确实有一个限制 - 例如,菜单条目中包含的文本是按命令定义的,因此如果您需要(或稍后可能添加)菜单,则此方法可能不是最好的选择(我经常将按钮标签绑定到命令文本中)。 - Bob Sammers
@Bob,这是一个简单的switch语句,限制在于每个表单都会使用所有快捷键组合,这将反映出命令参数字符串。在这种情况下,即使路由命令也无法帮助你。 - Waleed A.K.
1
抱歉@Waleed,我不明白你的意思。也许我表达得不够清楚。Guffa和Heinzi在他们的答案中都使用了RoutedUICommand。这个类有一个Text属性,从中取出文本以自动填充调用该命令的菜单条目(并且可以在其他地方使用——我经常通过共享模板将按钮文本绑定到它上面)。如果您使用一个命令(比如“帮助”),所有菜单项都将具有在Help.Text中定义的相同的单个文本片段。根据您喜欢的架构,这可能不是一个大问题,但我认为这是需要注意的事情。 - Bob Sammers
抱歉,@Bob,我误解了你,谢谢你的解释。 - Waleed A.K.

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