Я создаю приложение, используя шаблон проектирования MVVM, и я хочу использовать RoutedUICommands, определенные в классе ApplicationCommands. Так как свойство CommandBindings для View (чтение UserControl) не является DependencyProperty, мы не можем напрямую связывать CommandBindings, определенные в ViewModel, с View. Я решил это путем определения абстрактного класса View, который связывает это программно, на основе интерфейса ViewModel, который гарантирует, что каждый ViewModel имеет ObservableCollection of CommandBindings. Это все работает нормально, однако, в некоторых сценариях я хочу выполнить логику, которая определена в разных классах (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);
}
На первый взгляд все, что я хотел сделать, - это предыдущий код, но 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.Executed, обе части кода do выполняются, но не в правильном порядке.
Я думаю, что это ошибка в .Net, потому что вы должны иметь возможность добавить обработчик в PreviewExecuted и Executed и выполнять их по порядку, если вы не помечаете событие как обработанное.
Кто-нибудь может подтвердить это поведение? Или я не прав? Есть ли обходной путь для этой ошибки?