在同一个控件上,我可以为同一命令使用多个CommandBindings吗?

9
我有一个UserControl,它将CommandBinding添加到其CommandBindings集合中以处理特定命令。稍后我在窗口中使用此控件,并希望为该相同的控件添加另一个绑定来添加其他行为。然而,问题在于当我这样做时,似乎当我向控件的CommandBindings集合中添加另一个CommandBinding以处理相同的命令时,它会替换先前已经做出的任何绑定。因此,看起来控件每次只能有一个CommandBinding,是这样吗?
请参见下面的示例代码,尝试为相同的保存命令设置两个CommandBindings。
<Window x:Class="MultipleCommandBindings.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.CommandBindings>
    <CommandBinding Command="Save"
                    Executed="CommandBinding_Executed" />
    <CommandBinding Command="Save"
                    Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<Grid>
    <Button Height="23"
            HorizontalAlignment="Right"
            Margin="0,0,25,88"
            Name="button1"
            VerticalAlignment="Bottom"
            Width="75"
            Command="Save">Button</Button>
</Grid>

原本我期望在编写这段代码时会出现编译时或者运行时异常,但出乎意料的是它没有报错。接下来我感到失望的是,我的CommandBinding_Executed处理程序只被调用了一次,而不是我所希望的两次。

更新: 经过一些测试,似乎我的第二个CommandBinding并没有覆盖我的第一个CommandBinding,相反,即使我在事件处理程序中没有将Handled设置为true,第一个命令绑定也会捕获命令。我相信,解决我的问题的方法是理解为什么即使没有将Handled设置为true,路由命令也无法在第一个处理程序之后传播。

更新: 我发现了这个非常有用的信息,它证实了WPF中命令路由背后一些奇怪的行为。

更新: 关于如何解决似乎每个命令只能有一个有效的CommandBinding的问题,一个想法是,似乎默认的CommandBinding类将Executed和CanExecute公开为事件,就像所有事件一样,可以有多个处理程序。那么,除了标准的CommandBindings.Add方法之外,还有其他的方法可以添加命令的其他处理程序。也许这可以通过Control类的扩展方法和自定义的CompositeCommandBinding类来实现,后者允许我们在一个主绑定中聚合多个绑定。


我能想到的唯一解决方案是订阅按钮的Click事件,然后将View的DataContext强制转换为其ViewModel,并使用该变量手动执行2个命令。 - Stefan Vasiljevic
2个回答

3

到目前为止,我只能想出一个解决此问题的变通方法,即在逻辑树的两个不同级别处理命令。在下面的示例中,我在我的网格中处理“保存”命令,然后再次在窗口元素中处理。

<Window x:Class="MultipleCommandBindings.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.CommandBindings>
    <CommandBinding Command="Save"
                    Executed="CommandBinding_Executed2"/>
</Window.CommandBindings>
<Grid>
    <Grid.CommandBindings>
        <CommandBinding Command="Save"
                        Executed="CommandBinding_Executed1" />
    </Grid.CommandBindings>

    <Button Height="23"
            HorizontalAlignment="Right"
            Margin="0,0,25,88"
            Name="button1"
            VerticalAlignment="Bottom"
            Width="75"
            Command="Save">Button</Button>
</Grid>

为了让这个工作正常,我的代码背后还需要手动将命令执行传递到上一层。
    private void CommandBinding_Executed1(object sender, ExecutedRoutedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Executed 1!");
        var command = e.Command as RoutedUICommand;
        command.Execute(e.Parameter, this);
    }

    private void CommandBinding_Executed2(object sender, ExecutedRoutedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Executed 2!");
    }

如果有更好的想法来监控命令并让它自然地向上传播,请告诉我,否则我可能只能采用这种解决方法。

1

我不知道你问题的答案,但使用Reflector听起来对我来说是合理的。

我想知道你为什么要这样做?也许使用组合模式来聚合行为比尝试组合命令绑定更有意义?


1
+1 认为在视图中应该只有一个绑定,并且让多个行为在堆栈的深层被调用。 - Curt Nichols
在我的情况下,我有一个用户控件,可以是多种不同类型的命令来源,这些命令来自内部控件而进行传播。在用户控件内部,我希望能够意识到任何这些命令被发出,以便我可以使我的ViewModel启动一些其他流程,最终结果将通过我的用户控件(视图)公开。我可以绑定到我正在公开的属性,并在它更改时查看它,以便知道它何时准备好使用,但我需要了解开始整个过程的实际命令类型的信息。 - jpierson

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