默认命令目标是什么

本文关键字:是什么 目标 命令 默认 | 更新日期: 2023-09-27 18:34:08

在"WPF 控件开发"一书中,关于充当默认命令目标角色的元素有两种不同的想法:

第 258 页» 命令目标是命令所在的对象 提出。接口包含命令目标属性 可以设置为特定对象。默认情况下,命令源 本身被视为命令目标。

第 262 页» 默认情况下,如果未设置命令目标,则元素 使用键盘焦点。

此外,在本教程中,我们可以不定义菜单项和按钮命令目标,而只有菜单项(即而不是按钮(可以真正检测命令目标。那么默认命令目标是什么?!

默认命令目标是什么

基于一些不同的测试用例以及@dowhilefor和@hbarck的答案,我得出结论,每种情况都有一个特定的行进路径。

指定的命令目标:它从CommandTarget开始,向可视化树的根元素查找已绑定命令的第一个(最近(元素。(它仅在此路径上查找此元素。结论:

  1. sender:绑定了命令(带有 CommandBinding(的CommandTarget容器元素。
  2. e.source:指定为 CommandTarget 的元素。

未指定的命令目标:它从聚焦(在CommandSource范围内(指向可视化树根元素的元素开始,以查找已绑定命令的第一个(最近(元素。在此条件下,聚焦元素将被确定为 CommandTarget 。结论:

  1. sender:绑定命令(带有 CommandBinding 标记(的焦点元素的容器。
  2. e.来源:聚焦元素。

断章取义 我不明白第一个突出显示的句子是什么意思,但我认为这是错误的。另一方面,第二句是对的

MSDN:

如果未定义命令目标,则具有键盘焦点的元素 将用作命令目标。

如果您希望命令对某些内容进行操作,例如当前焦点文本框上的粘贴命令,这将非常有用。您希望粘贴命令始终有效,无论哪个文本框或哪个其他控件具有焦点,这都可以实现。值得指出的是,关于菜单,还有另一个概念需要记住,称为FocusScope。在 WPF 中执行命令有时可能很棘手,请考虑一个不占用文本框焦点的保存按钮,因此不刷新 Text 属性(因为它只更新 focuslost 上的目标绑定(。但请记住,CommandTarget只适用于RoutedCommands,不适用于"简单"的ICommand。

关于您的教程视频,还没有看过:此概念适用于所有不采用键盘焦点本身的命令源。

总而言之:只要命令是路由命令,CommandTarget 就是当前键盘焦点元素,否则会被忽略。

我想我刚刚明白这意味着什么:

如果元素可聚焦,则无法检测到未定义的路由命令 自动定位。

如果元素可聚焦,则意味着它在激活时将始终具有键盘焦点以引发命令。因此,如果它具有命令的命令绑定,它将始终自行处理它,如果没有,它将始终被禁用。

但是,您可以通过在控件的容器上将 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>

这里似乎缺少一点:给定命令的 CommandTarget 只能是为该命令定义 CommandBinding 的对象。

编辑:澄清并更正了以下段落,以免在系统中留下误导性信息。

命令路由是事件路由的一个特例,即在逻辑树中上下移动的事件:实现 ICommandSource 接口的控件,如 InputBindings、Button 或 MenuItems,是 CommandSources。如果他们引发命令,则会导致 RoutedEvent 在 CommandTarget 启动。这通常是具有键盘焦点的元素。事件沿逻辑树向上行进,直到到达根目录。沿此方式具有命令的命令绑定的所有元素都有机会处理该命令,尽管通常处理该命令的第一个元素获胜并停止路由过程。这甚至可能是 CommandSource 本身,如果它有一个命令的 CommandBinding,这可能是你的第一个引号的内容。如果一个元素处理事件,sender 参数将是定义 CommandBinding 的元素,而事件的 RoutedEventArgs 的 Source 属性将是事件启动路由的元素,即 CommandTarget。

为了使混淆完全,ICommandSource 接口定义了一个名为 CommandTarget 的属性。此属性适用于以下情况:您希望短路命令路由,并且希望有一个特殊控件来处理命令,而不管键盘焦点在哪里。在这种情况下,您可以在有问题的按钮或菜单项上编写类似 CommandTarget="{Binding ElementName=MyCommandTargetControl}" 的内容。同样,您必须确保此控件具有命令的命令绑定,否则该命令将被永久禁用。