有人知道 MenuItem 上的 CommandTarget 属性可以用来做什么吗?该文档说以下内容:
当与 RoutedCommand 一起使用时,命令目标是引发 Executed 和 CanExecute 事件的对象。如果未设置 CommandTarget 属性,则具有键盘焦点的元素将用作目标。
但是,在运行时,CommandTarget 的值在命令的 Execute 处理程序中无处可见。sender 是 CommandBinding 所属的窗口。ExecutedRoutedEventArgs 充满了对菜单项远祖的引用。
这里的目标是实现一个命令,该命令从各种不同的网格、列表等各种不同的上下文菜单中执行——它们都包含支持特定界面的项目。它们的上下文菜单不同,但有一些共同的命令。无论您单击什么,通用命令都使用相同的 Executed 和 CanExecute 处理程序,因为“Foo”命令执行“Foo”。处理程序确定您单击的任何网格/列表的所选项目是什么,尝试将其转换为接口,如果它具有该接口,则对其执行某些操作(如果给定命令使用的接口不支持该项目,该命令被禁用)。如果我将 ContextMenu 或 MenuItem 作为发件人,我可以获得 PlacementTarget 并且我知道用户点击了什么,但这只有在我在 ContextMenu 的 XAML 定义中定义 CommandBinding 时才有效——这意味着在使用该命令的每个 ContextMenu 中复制“n”整个 XAML 块,并在每个视图类中重新定义处理程序. 这不是我想要维护的混乱。
似乎在这种情况下,没有独立于语言的理由多次编写这些处理程序,或者将每个处理程序与给定命令关联多次。但据我所知,XAML 似乎希望您将处理程序和目标绑定在一起。你能绑定一次处理程序然后潜入不同的目标吗?
更新:我通过将命令放在静态 Command 类中,将处理程序放在非静态类中(主视图,无关紧要)并编写静态 Command.GetCommandBinding(command) 方法来实例化并返回一个CommandBinding 用于您传入的命令。因此,如果我想在网格 Bar 上使用命令 Foo,在 Bar 所在视图的构造函数中,我只需调用它:
Bar.CommandBindings.Add(Commands.GetCommandBinding(Commands.Foo));
然后,当将 Bar 分配给属于 Bar 的 ContextMenu 的 MenuItem 的 Command 属性时,Bar 作为命令上的 Executed 和 CanExecute 事件的发送者传递。
无法在 XAML 中进行绑定,因为处理程序必须是 View 类的成员。设计人员投入这么多工作来帮助我们重用命令的名称,同时让重用实际的命令变得如此痛苦,比如命令的 CODE,但无论如何,这似乎很奇怪。这不是微软做过的最愚蠢的事情,XAML 的其余大部分都非常棒(恕我直言)。
另一种解决方案:将菜单项定义为独立于上下文菜单的资源,并重用整个菜单项。这是在 Resources.xaml 中,我可以将其作为合并字典包含在其他 XAML 文件中。事件处理程序位于 Resources.cs 中。消费者可以使用 GridContextMenu,或者以同样的方式将 CtxMenuItem_EmailDocument 插入到他们自己的上下文菜单中。
<MenuItem Command="{x:Static vw:Commands.EmailDocument}"
x:Key="CtxMenuItem_EmailDocument">
<MenuItem.CommandBindings>
<CommandBinding Command="{x:Static vw:Commands.EmailDocument}"
Executed="EmailDocument_Executed"
CanExecute="EmailDocument_CanExecute"
/>
</MenuItem.CommandBindings>
</MenuItem>
<ContextMenu x:Key="GridContextMenu" x:Shared="true">
<!-- other items -->
<StaticResource ResourceKey="CtxMenuItem_EmailDocument" />
<!-- other items -->
</ContextMenu>
CommandTarget 似乎在 Button 上表现出完全不同的行为。如果它们是在单独的文件中定义的,或者作为资源定义的,或者......无论如何,无论是那个,还是 CommandBindings 的行为完全不同。