您如何被动响应 RoutedCommand?

How can you passively respond to a RoutedCommand?

我们的应用程序基于一堆页面,这些页面只是 FrameworkElement 的子类。主要 window 维护该堆栈并使用内置 Close 命令通过简单地将它们从堆栈中弹出来关闭它们。

现在在某些情况下,要关闭(或弹出堆栈)的元素需要先进行一些清理。让该页面也收听 Close 事件似乎是正确的做法。

现在,由于该页面实际上会在 window 之前获取事件(Close 命令是通过 'bubbling event' 实现的)我们认为我们所要做的就是设置页面上的命令绑定,然后在处理程序中,将 e.Handled 设置为 false,它将继续到 window.

这是页面中的代码(InitializeCommands 从构造函数中调用)...

private void InitializeCommands(){

    CommandBindings.Add(
        new CommandBinding(ApplicationCommands.Close, Close_Execute, Close_CanExecute)
    );
}

private void Close_CanExecute(object sender, CanExecuteRoutedEventArgs e){
    // True executes this handler, but blocks the one in the window
    // False executes the one in the window, but ignores this one
    e.CanExecute = true;

    // Doesn't seem to have any effect
    e.Handled = false;
}
private void Close_Execute(object sender, ExecutedRoutedEventArgs e){
    Console.WriteLine("I want to respond passively!");
    // Doesn't seem to have any effect
    e.Handled = false;
}

但是,无论我们将 属性 设置为什么,该命令都不会到达主 window。如果我们删除页面中的命令绑定,它会再次运行,证明页面正在吞下命令,而不管 属性.

那么要怎么做才能让页面被动监听Close事件呢?

是的,CommandBinding 吃命令。以下是其实施的摘录:

internal void OnExecuted(object sender, ExecutedRoutedEventArgs e)
{
    if (!e.Handled)
    {
        if (e.RoutedEvent == CommandManager.ExecutedEvent)
        {
            if (this.Executed != null && CheckCanExecute(sender, e))
            {
                this.Executed(sender, e);
                e.Handled = true;
            }
        }
        else if (this.PreviewExecuted != null && CheckCanExecute(sender, e))
        {
            this.PreviewExecuted(sender, e);
            e.Handled = true;
        }
    }
}

如您所见,如果您 return 为 CanExecute 设置为真,命令将被吃掉。

您可能想看看 CompositeCommand。那更符合你的口味。您创建一个绑定到框架的全局 CompositeCommand,然后不同的视图可以附加到它。不同的实现可以有不同的酷方式来确定命令的多个订阅者的行为方式。 IE。 all must return canExecute, any must return, only goes to the active view等

编辑:CompositeCommand 最初是 Prism 的一部分,但您可以找到一个独立的实现或者只是从 Prism 本身中提取一个:

https://github.com/PrismLibrary/Prism/blob/master/Source/Prism/Commands/CompositeCommand.cs

另一个想法是查看 AddHandler() 方法。这样您就可以为所有子事件添加一个事件处理程序。 IE。对于我的面包屑控制,我可以这样做:

AddHandler(BreadcrumbSplitButton.ClickEvent, new RoutedEventHandler(OnBreadcrumbSplitButtonClick));

在 BreadCrumb class 中监听所有子 BreadcrumbSplitButtons 的 ClickEvent。