更新:フォーカスが実際の質問ではなくMVVMになったので、更新しています。
CanExecute
のDelegateCommand
に問題があります。 RaiseCanExecuteChanged
を呼び出す前に更新されませんが、これは望ましい動作ですか?
この問題を再現した簡単なサンプルプロジェクトをここにアップロードしました: http://dl.dropbox.com/u/39657172/DelegateCommandProblem.Zip
問題はこれです、私はこのような2つのButtons
を持っています。 1つはCommand
をRelayCommand
実装にバインドし、もう1つはDelegateCommand
のPrism実装にバインドします。
<Button Command="{Binding DelegateSaveCommand}"/>
<Button Command="{Binding RelaySaveCommand}"/>
ViewModel ICommands
DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate);
RelaySaveCommand = new RelayCommand(param => Save(), param => CanSaveRelay);
およびCanExecute
メソッド/述語
public bool CanSaveDelegate()
{
return HasChanges;
}
public bool CanSaveRelay
{
get { return HasChanges; }
}
どちらもプロパティHasChanges
を使用しています。 HasChanges
が更新されると、CanSaveRelay
のみが更新されます。これは本来あるべき姿ですか?
すでに述べたように、これはDelagateCommand
の意図された動作であり、バグではありません。 DelegateCommand
はCanExecuteChanged
イベントを自動的に発生させません。必要に応じて、RaiseCanExecuteChanged
を呼び出して、手動でそのイベントを発生させる必要があります。一方、RelayCommand
はそのためのCommandManager.RequerySuggested
イベントを中継します。このイベントは、ユーザーがどこかをクリックするか、ボタンを押すたびに発生します。
あまり便利ではない場合や、RaiseCanExecuteChanged
を呼び出す適切な場所がない場合(シナリオのように、モデルでPropertyChanged
イベントをサブスクライブする必要があるなど)、次のように作成しましたラップされたコマンドのCanExecute
メソッドがCommandManager.RequerySuggested
イベントで自動的に実行されることを保証する単純なラッパー:
public class AutoCanExecuteCommandWrapper : ICommand
{
public ICommand WrappedCommand { get; private set; }
public AutoCanExecuteCommandWrapper(ICommand wrappedCommand)
{
if (wrappedCommand == null)
{
throw new ArgumentNullException("wrappedCommand");
}
WrappedCommand = wrappedCommand;
}
public void Execute(object parameter)
{
WrappedCommand.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return WrappedCommand.CanExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
次のように使用できます。
DelegateSaveCommand = new AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate));
DelegateCommand
に固執したい場合は、ObservesCanExecute
を使用できます。
DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate).ObservesCanExecute(CanSaveDelegate);
CanExecuteチェックにプロパティを使用している場合は、ObservesProperty
も使用できることに注意してください。ただし、プロパティはNotifyPropertyChangedを呼び出す必要があります。
Prismが提供するDelegateCommandには、CanExecuteイベントを発生させないバグがあります。 Prismフレームワークによって提供されるDelegateCommand
クラスに飛び込むまで、私は1日壁に頭をぶつけました。私はコードを持っていませんが、解決策を少し投稿することができます。
別の方法は、他のRelayCommandフレームワークの1つを使用することです。
編集
コードを再投稿するのではなく、解決策を提供する他のSO質問があります:
そして、ケントB.には良い記事があります: MVVMインフラストラクチャ:DelegateCommand