我正在使用MVVM设计模式构建应用程序,并且想利用ApplicationCommands类中定义的RoutedUICommands。由于 View (读取用户控件)的CommandBindings属性不是DependencyProperty,因此我们无法将ViewModel中定义的CommandBindings直接绑定(bind)到View。我通过定义一个抽象View类来解决此问题,该View类基于ViewModel接口(interface)以编程方式绑定(bind)了该类,以确保每个ViewModel都具有CommandBindings的ObservableCollection。这一切都很好,但是,在某些情况下,我想执行在不同类(View和ViewModel)相同命令中定义的逻辑。例如,保存文档时。

在ViewModel中,代码将文档保存到磁盘:

private void InitializeCommands()
{
    CommandBindings = new CommandBindingCollection();
    ExecutedRoutedEventHandler executeSave = (sender, e) =>
    {
        document.Save(path);
        IsModified = false;
    };
    CanExecuteRoutedEventHandler canSave = (sender, e) =>
    {
        e.CanExecute = IsModified;
    };
    CommandBinding save = new CommandBinding(ApplicationCommands.Save, executeSave, canSave);
    CommandBindings.Add(save);
}

乍一看,以前的代码只是我想要做的,但是文档绑定(bind)到的 View 中的TextBox仅在失去焦点时才更新其Source。但是,我可以通过按Ctrl + S来保存文档而不会失去焦点。这意味着文档在更改之前保存在源中的“更新”之前,从而有效地忽略了更改。但是由于出于性能原因,将UpdateSourceTrigger更改为PropertyChanged是不可行的选项,因此在保存之前,其他一些操作必须强制进行更新。所以我想,让我们使用PreviewExecuted事件来强制在PreviewExecuted事件中进行更新,如下所示:
//Find the Save command and extend behavior if it is present
foreach (CommandBinding cb in CommandBindings)
{
    if (cb.Command.Equals(ApplicationCommands.Save))
    {
        cb.PreviewExecuted += (sender, e) =>
        {
            if (IsModified)
            {
                BindingExpression be = rtb.GetBindingExpression(TextBox.TextProperty);
                be.UpdateSource();
            }
            e.Handled = false;
        };
    }
}

但是,即使我将Handled属性显式设置为false,将处理程序分配给PreviewExecuted事件似乎也完全取消了该事件。因此,我在之前的代码示例中定义的executeSave事件处理程序不再执行。请注意,当我将cb.PreviewExecuted更改为cb.Executed时,两段代码都会执行,但执行顺序不正确。

我认为这是.Net中的错误,因为只要您不将事件标记为已处理,就应该能够将处理程序添加到PreviewExecuted和Executed并按顺序执行它们。

任何人都可以确认这种行为吗?还是我错了?有此Bug的解决方法吗?

最佳答案


如果查看CommandBinding类的源代码,则有OnExecuted()方法负责调用通过CommandBinding为PreviewExecuted和Executed事件注册的处理程序。那里有一点:

PreviewExecuted(sender, e);
e.Handled = true;
这会将事件设置为在您的PreviewExecuted处理程序返回后立即处理,因此不会调用Executed。

请注意,仅当通过CommandBinding注册处理程序时,此方法才有效。
如果仍然要同时运行PreviewExecuted和Executed,则有两个选择:
  • 您可以从PreviewExecuted处理程序中调用路由命令的Execute()方法。只是考虑一下-在PreviewExecuted完成之前调用Executed处理程序时,您可能会遇到同步问题。对我来说,这似乎不是一个好方法。
  • 您可以通过CommandManager.AddPreviewExecutedHandler()静态方法分别注册PreviewExecuted处理程序。这将直接从UIElement类调用,并且将不涉及CommandBinding。 EDIT 2: Look at the point 4 at the beginning of the post - these are the events we're adding the handlers for.

  • 从外观上看-这样做是故意的。为什么?一个人只能猜...

    关于c# - RoutedUICommand PreviewExecuted错误?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2281825/

    10-12 00:36