WPF MenuItem.CommandTarget
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17068055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF MenuItem.CommandTarget
提问by 15ee8f99-57ff-4f92-890c-b56153
Does anybody know what the CommandTarget property on a MenuItem can be used for? The documentation says the following:
有人知道 MenuItem 上的 CommandTarget 属性可以用来做什么吗?文档说明如下:
When used with a RoutedCommand, the command target is the object on which the Executed and CanExecute events are raised. If the CommandTarget property is not set, the element with keyboard focus will be used as the target.
当与 RoutedCommand 一起使用时,命令目标是在其上引发 Executed 和 CanExecute 事件的对象。如果未设置 CommandTarget 属性,则具有键盘焦点的元素将用作目标。
However, at runtime, the value of CommandTarget is nowhere to be seen in the Execute handler for the command. sender is the window the CommandBinding belongs to. ExecutedRoutedEventArgs is full of references to distant ancestors of the menu item.
但是,在运行时,CommandTarget 的值在命令的执行处理程序中无处可见。sender 是 CommandBinding 所属的窗口。ExecutedRoutedEventArgs 充满了对菜单项的远祖的引用。
The goal here is to implement a command which is executed from a variety of different context menus on a variety of different grids, lists, whatever -- they all contain items which support a particular interface. Their context menus differ, but have some commands in common. The common commands use the same Executed and CanExecute handlers regardless of what you click on, because the "Foo" command does "Foo". The handlers figure out what the selected item is for whatever grid/list you clicked on, try to cast it to an interface, and do something with it if it's got that interface (if the interface consumed by a given command isn't supported by the item, that command is disabled). If I get the ContextMenu or MenuItem as sender, I can get the PlacementTarget and I know what the user clicked on, but that only works if I define the CommandBinding in the XAML definition of the ContextMenu -- which means copy'n'pasting that whole block of XAML in every ContextMenu where the command is used, and redefining the handlers in every one of those view classes. That's not a mess I want to maintain.
这里的目标是实现一个命令,该命令从各种不同的网格、列表等各种不同的上下文菜单中执行——它们都包含支持特定界面的项目。它们的上下文菜单不同,但有一些共同的命令。无论您单击什么,常用命令都使用相同的 Executed 和 CanExecute 处理程序,因为“Foo”命令执行“Foo”。处理程序找出您单击的任何网格/列表的所选项目,尝试将其转换为接口,如果有该接口,则对其进行处理(如果给定命令使用的接口不受支持)项,该命令已禁用)。如果我将 ContextMenu 或 MenuItem 作为发件人,我可以获得 PlacementTarget 并且我知道用户点击了什么,但这只有在我在 ContextMenu 的 XAML 定义中定义 CommandBinding 时才有效——这意味着在使用该命令的每个 ContextMenu 中复制“n”粘贴整个 XAML 块,并在每个视图类中重新定义处理程序. 这不是我想要维护的混乱。
It seems like this is a case where there's no language-independent reason to write those handlers more than once, or associate each one with a given command more than once. But as far as I can tell, it appears that XAML wants you to bind the handlers and the target all together. Can you bind the handlers once and then sneak in a different target?
在这种情况下,似乎没有独立于语言的原因多次编写这些处理程序,或者将每个处理程序与给定命令关联不止一次。但据我所知,XAML 似乎希望您将处理程序和目标绑定在一起。你能绑定一次处理程序然后潜入不同的目标吗?
UPDATE: I resolved this by putting the commands in a static Command class, the handlers in a non-static class (the main view, not that it matters), and writing a static Command.GetCommandBinding(command) method which instantiates and returns a CommandBinding for the command you pass in. So if I want to use command Foo on grid Bar, in the constructor for the view where Bar lives I just call this:
更新:我通过将命令放在静态 Command 类中,将处理程序放在非静态类(主视图,无关紧要)中并编写静态 Command.GetCommandBinding(command) 方法来实例化并返回一个您传入的命令的 CommandBinding。因此,如果我想在网格 Bar 上使用命令 Foo,则在 Bar 所在视图的构造函数中,我只需调用以下代码:
Bar.CommandBindings.Add(Commands.GetCommandBinding(Commands.Foo));
Bar is then passed as the sender for the Executed and CanExecute events on the command when it is assigned to the Command property of a MenuItem belonging to Bar's ContextMenu.
然后,当将 Bar 分配给属于 Bar 的 ContextMenu 的 MenuItem 的 Command 属性时,将 Bar 作为命令上 Executed 和 CanExecute 事件的发送者传递。
Can't do the binding in XAML because the handlers have to be members of the View class. It seems odd that the designers put this much work into helping us reuse the NAMES of commands while making it so painful to reuse the actual, like CODE for the command, but whatever. It's not the dumbest thing Microsoft ever did, and most of the rest of XAML is pretty great (IMHO).
无法在 XAML 中进行绑定,因为处理程序必须是 View 类的成员。设计者投入这么多工作来帮助我们重用命令的名称,同时让重用实际的命令变得如此痛苦,例如用于命令的 CODE,这似乎很奇怪,但无论如何。这不是微软做过的最愚蠢的事情,其余的大部分 XAML 都非常棒(恕我直言)。
Another solution: Define menu items as resources independently of context menus, and reuse the whole menu items. This is in Resources.xaml, which I can include in other XAML files as a merged dictionary. The event handlers are in Resources.cs. Consumers can use GridContextMenu, or insert CtxMenuItem_EmailDocument into their own context menus the same way.
另一种解决方案:将菜单项定义为独立于上下文菜单的资源,并重用整个菜单项。这是在 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 seems to exhibit completely different behavior on a Button. Either that, or CommandBindings behave completely differently if they're defined in separate file, or as a resource, or... whatever.
CommandTarget 似乎在按钮上表现出完全不同的行为。如果它们是在单独的文件中定义的,或者作为资源定义的,或者......无论如何,那么或者 CommandBindings 的行为完全不同。
回答by hbarck
Everything AndrewS has written is correct, I just want to add that the CommandTarget will be the senderof the Executed / CanExecute events. In order to be able to handle the command, the CommandTarget needs a CommandBinding for the Command in question.
AndrewS 写的一切都是正确的,我只想补充一点,CommandTarget 将是Executed / CanExecute 事件的发送者。为了能够处理该命令,CommandTarget 需要针对所讨论的命令的 CommandBinding。
Minimum example:
最小示例:
<StackPanel>
<Button Command="Open" CommandTarget="{Binding ElementName=TestTextBox}">Open</Button>
<TextBox x:Name="TestTextBox">
<TextBox.CommandBindings>
<CommandBinding Command="Open" Executed="CommandBinding_Executed"/>
</TextBox.CommandBindings>
</TextBox>
</StackPanel>
回答by AndrewS
The CommandTargetis the element on which the CommandManager class will start the routing of the CanExecute and Execute events when the associated Command is a RoutedCommand. So it is not really going to show up as any of the parameters of the event args for the CanExecute/Execute - actually it may be the OriginalSourcebut I wouldn't rely on that because if the CommandManager reroutes the command (because it enters a new FocusScopeas it traverses up the tree) then it will reroute and the OriginalSource for the eventargs for rerouted event will then be that element that it rerouted to.
该CommandTarget是在其上的CommandManager类将启动CanExecute的路由和执行活动时,相关的命令是的RoutedCommand的元素。所以它不会真正显示为 CanExecute/Execute 的事件 args 的任何参数 - 实际上它可能是OriginalSource但我不会依赖它,因为如果 CommandManager 重新路由命令(因为它输入了new FocusScope在它向上遍历树时)然后它将重新路由,然后重新路由事件的 eventargs 的 OriginalSource 将是它重新路由到的那个元素。
Typically you do not set the CommandTarget for RoutedCommands that can be handled by multiple element types - e.g. the ApplicationCommands like Cut/Copy/Paste where you want the control the end user is interacting with to receive and respond to the command. However if you have a case where regardless of what element the end user has focused, you want to make sure that the RoutedCommand that you have set for the Command property of that ICommandSource (the MenuItem in this case) is executed on a specific element instance then you would set the CommandTarget to that element (typically using an ElementName Binding).
通常,您不会为可由多种元素类型处理的 RoutedCommands 设置 CommandTarget - 例如,像剪切/复制/粘贴这样的应用程序命令,您希望最终用户与之交互的控件接收和响应命令。但是,如果您有这样一种情况,无论最终用户关注什么元素,您都希望确保为该 ICommandSource(在本例中为 MenuItem)的 Command 属性设置的 RoutedCommand 在特定元素实例上执行然后您将 CommandTarget 设置为该元素(通常使用 ElementName 绑定)。
Edit:Since you have changed the question, I'll augment my answer. If you want to handle the CanExecute and Execute for certain RoutedCommands on particular class types then what you would want to do is to use the CommandManager class - specifically it's RegisterClassCommandBindingmethod - to register your global Execute/CanExecute handler for your specific RoutedCommands.
编辑:既然你已经改变了问题,我会增加我的答案。如果您想处理特定类类型上某些 RoutedCommands 的 CanExecute 和 Execute,那么您想要做的是使用 CommandManager 类 - 特别是它的RegisterClassCommandBinding方法 - 为您的特定 RoutedCommands 注册您的全局 Execute/CanExecute 处理程序。
回答by Cool Blue
Just for the record, the CommandTarget, in the context of a MenuItem, sets the OriginalSourceproperty on ExecutedRoutedEventArgsreceived by the Executedand CanExecutehandlers.
只是为了记录, the CommandTarget,在 a 的上下文中MenuItem,设置由and处理程序接收的OriginalSource属性。ExecutedRoutedEventArgsExecutedCanExecute
So the "Target" is the "Source"
(Much in the same way that a camel is a horse designed by a committee)
所以“目标”是“源”
(就像骆驼是委员会设计的马一样)
The Sender, as stated by @EdPlunket, is the site of the command bindings (as is e.Sourcetoo actually).
的Sender,由@EdPlunket说,是命令绑定的网站(如e.Source太实际)。
So, to answer the original question, the CommandTargetof a MenuItemis passed as a reference, to the Executedand CanExecutehandlers of a command, on the event args (ExecutedRoutedEventArgsand CanExecuteRoutedEventArgs) on their OriginalSourceproperty. So you can use that reference to do whatever you like on the target.
因此,为了回答最初的问题,CommandTargeta 的 aMenuItem作为引用传递给命令的ExecutedandCanExecute处理程序,在其属性上的事件 args ( ExecutedRoutedEventArgsand CanExecuteRoutedEventArgs) 上OriginalSource。因此,您可以使用该引用对目标执行任何您喜欢的操作。
Working Example
工作示例
The CommandTargetof the MenuItemis controlled by the ComboBox selection
该CommandTarget的MenuItem是组合框选择控制
MainWindow.xaml
主窗口.xaml
<Window x:Class="CommandTarget.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommandTarget"
Title="MainWindow" Height="200" Width="400">
<StackPanel x:Name="RootPanel">
<StackPanel.CommandBindings>
<CommandBinding x:Name="Pause" Command="Pause"
Executed="{x:Static local:MainWindow.OnButtonPause}"
CanExecute="{x:Static local:MainWindow.OnPauseCanExecute}" />
</StackPanel.CommandBindings>
<DockPanel>
<Menu DockPanel.Dock="Top" >
<MenuItem Header="Click Me" x:Name="Emitter"
Command="Pause"
CommandTarget="{Binding ElementName=Button2}" />
</Menu>
</DockPanel>
<StackPanel Name="Buttons">
<ToggleButton x:Name="Button1" Height="30" HorizontalAlignment="Stretch"
Content="Button1" />
<ToggleButton x:Name="Button2" Height="30" HorizontalAlignment="Stretch"
Content="Button2" />
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs
主窗口.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace CommandTarget
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//PAUSE COMMAND
// Static, binding callbacks
// Executed
public static ExecutedRoutedEventHandler
OnButtonPause = (sender, e) =>
{
e.Handled = ButtonPauseTarget(e, delegate(ToggleButton target)
{
if (!target.IsEnabled) return false;
var flag = target.IsChecked ?? false;
target.IsChecked = !flag;
return true;
});
};
// CanExecute
public static CanExecuteRoutedEventHandler
OnPauseCanExecute = (sender, e) => { e.CanExecute = true; };
// helper to extract the target from the event args
private static bool ButtonPauseTarget (RoutedEventArgs e,
Func<ToggleButton, bool> ex)
{
var target = e.OriginalSource as ToggleButton;
if (target == null) return false;
var handled = ex(target);
return handled;
}
}
}
Note: The Pause command is just a random choice, any of the commands supported by the source element, that is not used by any other will do.
注意:暂停命令只是一个随机选择,源元素支持的任何其他命令都没有使用的命令都可以。

