Error RoutedUICommand PreviewExecuted Bug?

I am creating an application using the MVVM design pattern and I want to use the RoutedUICommands defined in the ApplicationCommands class. Since the CommandBindings property for View (read UserControl) is not a DependencyProperty, we cannot directly associate the CommandBindings defined in the ViewModel with the view. I solved this by defining an abstract View class that binds it programmatically, based on the ViewModel interface, which ensures that each ViewModel has ObservableCollection CommandBindings. All this works fine, however in some scenarios I want to execute the logic defined in different classes (View and ViewModel) by the same command. For example, when saving a document.

In ViewModel, the code saves the document to disk:

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);
}

, - , , TextBox , , , . , 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;
        };
    }
}

PreviewExecuted, , , Handled false. , executeSave, , . , cb.PreviewExecuted cb. do, .

, .Net, PreviewExecuted Executed , .

- ? ? ?

+5
2

2: , :

  • UIElement CommandManager.TranslateInput() ( ).
  • CommandManager CommandBindings , , .
  • , CanExecute(), true, Executed().
  • RoutedCommand : CommandManager.PreviewCanExecuteEvent CommandManager.CanExecuteEvent ( PreviewExecutedEvent ExecutedEvent) UIElement, . .
  • UIElement , , CommandManager.OnCanExecute() CommandManager.CanExecute() ( , ).
  • CommandManager.OnCanExecute() CommandManager.OnExecute(), , CommandBinding. , CommandManager UIElement, , .

CommandBinding, OnExecuted(), , PreviewExecuted Executed CommandBinding. :

PreviewExecuted(sender, e); 
e.Handled = true;

PreviewExecuted, Executed .

1: CanExecute PreviewCanExecute :

  PreviewCanExecute(sender, e); 
  if (e.CanExecute)
  { 
    e.Handled = true; 
  }

Handled to true , , CanExecute. CanExecuteRoutedEventArgs CanExecute true PreviewCanExecute CanExecute.

ContinueRouting Preview - false, , .

, , CommandBinding.

, PreviewExecuted Executed , :

  • Execute() PreviewExecuted. - , Executed PreviewExecuted. .
  • PreviewExecuted CommandManager.AddPreviewExecutedHandler(). 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.

- . ? ...

+3

ContinueRouting:

foreach (CommandBinding cb in CommandBindings)
{
    if (cb.Command.Equals(ApplicationCommands.Save))
    {
        ExecutedRoutedEventHandler f = null;
        f = (sender, e) =>
        {
            if (IsModified)
            {
                BindingExpression be = rtb.GetBindingExpression(TextBox.TextProperty);
                be.UpdateSource();

                // There is a "Feature/Bug" in .Net which cancels the route when adding PreviewExecuted
                // So we remove the handler and call execute again
                cb.PreviewExecuted -= f;
                cb.Command.Execute(null);
            }
        };
        cb.PreviewExecuted += f;
    }
}
+1

All Articles