在“WPF控件开发”一书中,关于扮演默认命令目标的元素有两种不同的想法:
第 258 页»命令目标是发出命令的对象。ICommandSource 接口包含一个可以设置为特定对象的 CommandTarget 属性。默认情况下,命令源本身被视为命令目标。
第 262 页» 默认情况下,当未设置 CommandTarget 时,使用具有键盘焦点的元素。
此外,在本教程中,我们可以不定义菜单项和按钮命令目标,而只有菜单项(即不是按钮)可以真正检测命令目标。那么默认的命令目标是什么?!
基于一些更多不同的测试用例以及关于@dowhilefor 和@hbarck 的答案,我得出结论,每种情况都有一条特定的行进路径。
Specified CommandTarget:从CommandTarget
视觉树的根元素开始,寻找第一个(最近的)绑定了命令的元素。(它仅在此路径上查找此元素。)结论:
CommandTarget
容器元素(带CommandBinding
)。CommandTarget
。未指定的CommandTarget:它从聚焦(在CommandSource
范围内)的元素开始向可视树的根元素开始,找到第一个(最近的)绑定了命令的元素。在这种情况下,焦点元素将被确定为CommandTarget
。结论:
CommandBinding
标签)的焦点元素的容器。断章取义,我不明白第一个突出显示的句子是什么意思,但我认为它是错误的。另一方面第二句话是对的
女士号码:
如果未定义命令目标,则将具有键盘焦点的元素用作命令目标。
如果您希望命令对某些内容进行操作,例如在当前聚焦的文本框上粘贴命令,这将非常有用。您希望粘贴命令始终有效,无论哪个文本框或哪个其他控件具有焦点,这使这成为可能。值得指出的是,关于菜单,还有另一个概念需要记住,称为FocusScope。WPF 中的命令有时可能很棘手,考虑一个不获取文本框焦点的保存按钮,因此不刷新 Text 属性(因为它只更新 focuslost 上的目标绑定)。但请记住,CommandTarget
仅适用于RoutedCommands
,而不适用于“简单”ICommands。
关于您的教程视频,还没有看过:这个概念适用于所有不采用键盘焦点本身的 CommandSource。
所以得出结论:CommandTarget 是,只要Command 是一个RoutedCommand,当前键盘聚焦的元素,否则它被忽略。
这里似乎缺少一点:给定命令的 CommandTarget 只能是为该命令定义 CommandBinding 的对象。
编辑:澄清和更正以下段落,以免在系统中留下误导性信息。
命令路由是事件路由的一种特殊情况,即事件在逻辑树上上下移动:实现 ICommandSource 接口的控件,如 InputBindings、Buttons 或 MenuItems,是 CommandSources。如果他们提出命令,这会导致 RoutedEvent 在 CommandTarget 处开始。这通常是具有键盘焦点的元素。事件沿逻辑树向上传播,直到到达根。沿此方式,所有具有命令的 CommandBindings 的元素都有机会处理命令,尽管通常处理命令的第一个元素获胜并停止路由过程。这甚至可能是 CommandSource 本身,如果它有一个用于命令的 CommandBinding,那么这可能就是您的第一个引用。如果一个元素处理事件,
为了完全混淆,ICommandSource 接口定义了一个名为 CommandTarget 的属性。此属性适用于您想要短路命令路由的情况,并且想要一个特殊的控件来处理命令,无论键盘焦点在哪里。在这种情况下,您可以在相关的 Button 或 MenuItem 上编写类似 CommandTarget="{Binding ElementName=MyCommandTargetControl}" 的内容。同样,您必须确保此控件具有用于命令的 CommandBinding,否则该命令将被永久禁用。
我想我刚刚明白这意味着什么:
如果一个元素是可聚焦的,它不能自动检测未定义的路由命令目标。
如果一个元素是可聚焦的,这意味着当它被激活以发出命令时,它将始终具有键盘焦点。因此,如果它有一个用于命令的 CommandBinding,它总是会自己处理它,如果没有,它总是会被禁用。
但是,您可以通过在控件的容器上将 FocusManager.IsFocusScope 设置为 true 来解决此问题,就像在此 XAML 中一样:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:CommandRouting"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu IsMainMenu="True">
<MenuItem x:Name="TestMenuItem" Command="{x:Static my:MainWindow.TestCommand}"/>
</Menu>
<GroupBox x:Name="CommandBindingOnControlsGroupBox" Header="CommandBinding on Controls" Grid.Row="1">
<StackPanel>
<Button x:Name="CommandBindingOnButtonButton" Command="{x:Static my:MainWindow.TestCommand}" Content="CommandBinding on Button">
<Button.CommandBindings>
<CommandBinding Command="{x:Static my:MainWindow.TestCommand}" Executed="CommandBinding_Executed" PreviewExecuted="CommandBinding_Executed"/>
</Button.CommandBindings>
</Button>
<TextBox x:Name="CommandBindingOnTextBoxTextBox">
<TextBox.CommandBindings>
<CommandBinding Command="{x:Static my:MainWindow.TestCommand}" Executed="CommandBinding_Executed"/>
</TextBox.CommandBindings>
<TextBox.InputBindings>
<!-- provide alternate keyboard shortcut -->
<KeyBinding Key="{x:Static Key.P}" Modifiers="{x:Static ModifierKeys.Control}" Command="{x:Static my:MainWindow.TestCommand}"/>
</TextBox.InputBindings>
</TextBox>
<Button x:Name="CommandTargetOnButtonButton" Command="{x:Static my:MainWindow.TestCommand}" Content="CommandTarget on Button" CommandTarget="{Binding ElementName=CommandBindingOnControlsGroupBox}">
<Button.CommandBindings>
<CommandBinding Command="{x:Static my:MainWindow.TestCommand}" Executed="CommandBinding_Executed"/>
</Button.CommandBindings>
</Button>
</StackPanel>
</GroupBox>
<GroupBox x:Name="CommandBindingOnContainerGroupBox" Header="CommandBinding on Container" Grid.Row="2">
<GroupBox.CommandBindings>
<CommandBinding Command="{x:Static my:MainWindow.TestCommand}" PreviewExecuted="CommandBinding_Executed"/>
</GroupBox.CommandBindings>
<StackPanel x:Name="CommandBindingOnInnerContainerStackPanel">
<StackPanel.CommandBindings>
<CommandBinding Command="{x:Static my:MainWindow.TestCommand}" Executed="CommandBinding_Executed"/>
</StackPanel.CommandBindings>
<Button x:Name="CommandBindingOnContainerButton" Command="{x:Static my:MainWindow.TestCommand}" Content="CommandBinding on Two Containers">
</Button>
<TextBox x:Name="CommandBindingOnContainerTextBox">
<TextBox.InputBindings>
<!-- provide alternate keyboard shortcut -->
<KeyBinding Key="{x:Static Key.P}" Modifiers="{x:Static ModifierKeys.Control}" Command="{x:Static my:MainWindow.TestCommand}"/>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</GroupBox>
<GroupBox x:Name="OtherFocusScopeGroupBox" FocusManager.IsFocusScope="True" Header="Other FocusScope, No CommandBindings" Grid.Row="3">
<StackPanel >
<Button x:Name="OtherFocusScopeButton" Command="{x:Static my:MainWindow.TestCommand}" Content="Other FocusScope">
</Button>
<TextBox x:Name="OtherFocusScopeTextBox">
<TextBox.CommandBindings>
<CommandBinding Command="{x:Static my:MainWindow.TestCommand}" Executed="CommandBinding_Executed"/>
</TextBox.CommandBindings>
<TextBox.InputBindings>
<!-- provide alternate keyboard shortcut -->
<KeyBinding Key="{x:Static Key.P}" Modifiers="{x:Static ModifierKeys.Control}" Command="{x:Static my:MainWindow.TestCommand}"/>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</GroupBox>
</Grid>
</Window>