MVVM 設計パターンを使用してアプリケーションを構築しており、ApplicationCommands クラスで定義された RoutedUICommands を利用したいと考えています。View の CommandBindings プロパティ (UserControl を参照) は DependencyProperty ではないため、ViewModel で定義された CommandBindings を View に直接バインドすることはできません。すべての ViewModel が CommandBindings の ObservableCollection を持つことを保証する ViewModel インターフェイスに基づいて、これをプログラムでバインドする抽象 View クラスを定義することで、これを解決しました。これはすべて正常に機能しますが、一部のシナリオでは、異なるクラス (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);
}
一見したところ、前のコードだけで十分ですが、ドキュメントがバインドされている 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;
};
}
}
ただし、ハンドラーを PreviewExecuted イベントに割り当てると、Handled プロパティを明示的に false に設定した場合でも、イベントが完全にキャンセルされるようです。したがって、前のコード サンプルで定義した executeSave イベント ハンドラは実行されなくなります。cb.PreviewExecuted を cb.Executed に変更すると、両方のコードが実行されますが、正しい順序ではないことに注意してください。
イベントを処理済みとしてマークしない限り、PreviewExecuted と Executed にハンドラーを追加して順番に実行できるはずなので、これは .Net のバグだと思います。
誰でもこの動作を確認できますか? それとも私が間違っていますか?このバグの回避策はありますか?