Я создаю приложение с использованием шаблона проектирования MVVM, и я хочу использовать RoutedUICommands, определенные в классе ApplicationCommands. Поскольку свойство CommandBindings для View (read UserControl) не является DependencyProperty, мы не можем напрямую связать CommandBindings, определенные в ViewModel, с представлением. Я решил это, определив абстрактный класс View, который связывает это программно, на основе интерфейса ViewModel, который гарантирует, что у каждого ViewModel есть ObservableCollection CommandBindings. Все это прекрасно работает, однако в некоторых сценариях я хочу выполнить логику, определенную в разных классах (View и ViewModel) той же самой командой. Например, при сохранении документа.RoutedUICommand PreviewExecuted Bug?
В 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);
}
На первый взгляд предыдущий код все, что я хотел сделать, но 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 кажется отменить мероприятие в целом, даже когда я явно установить Обработано свойство false. Таким образом, обработчик события executeSave, который я определил в предыдущем примере кода, больше не выполняется. Обратите внимание, что когда я меняю cb.PreviewExecuted на cb.Сделал обе части кода do выполнить, но не в правильном порядке.
Я думаю, что это ошибка в .Net, потому что вы должны иметь возможность добавлять обработчик PreviewExecuted и Executed и выполнять их в порядке, если вы не отмечаете событие как обработанное.
Кто-нибудь может подтвердить это поведение? Или я ошибаюсь? Есть ли обходной путь для этой ошибки?
Сюжета утолщается ... Так что я смотрел на исходном коде вы упомянули, и они делают то же самое в OnCanExecute с PreviewCanExecute. Тем не менее, существует важное различие между CanExecuteRoutedEventArgs из OnCanExecute и ExecutedRoutedEventArgs из OnExecuted. Как и следовало ожидать, CanExecuteRoutedEventArgs содержит свойство ContinueRouting, которое делает именно это, но по какой-либо причине ExecutedRoutedEventArgs обойдется. На самом деле я действительно не могу обойти этот выбор от Microsoft. – elmar
Я думаю, что ContinueRouting не участвует в этом процессе - см. Мой EDIT 2 в сообщении. Что касается того, почему они сделали это так ...Посмотрите на две части метода CommandBinding.OnExecuted(), они почти точно такие же - это может быть классический случай копирования/вставки :), а затем это ошибка. Серьезно, хотя, я не думаю, что это так. Мне действительно нравится знать, в чем их причина. –