web-dev-qa-db-ja.com

DelegateCommandのCanExecuteロジック

更新:フォーカスが実際の質問ではなくMVVMになったので、更新しています。

CanExecuteDelegateCommandに問題があります。 RaiseCanExecuteChangedを呼び出す前に更新されませんが、これは望ましい動作ですか?

enter image description here

この問題を再現した簡単なサンプルプロジェクトをここにアップロードしました: http://dl.dropbox.com/u/39657172/DelegateCommandProblem.Zip

問題はこれです、私はこのような2つのButtonsを持っています。 1つはCommandRelayCommand実装にバインドし、もう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のみが更新されます。これは本来あるべき姿ですか?

13
Fredrik Hedblad

すでに述べたように、これはDelagateCommandの意図された動作であり、バグではありません。 DelegateCommandCanExecuteChangedイベントを自動的に発生させません。必要に応じて、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));
23
Pavlo Glazkov

DelegateCommandに固執したい場合は、ObservesCanExecuteを使用できます。

DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate).ObservesCanExecute(CanSaveDelegate);

CanExecuteチェックにプロパティを使用している場合は、ObservesPropertyも使用できることに注意してください。ただし、プロパティはNotifyPropertyChangedを呼び出す必要があります。

0
anhoppe

Prismが提供するDelegateCommandには、CanExecuteイベントを発生させないバグがあります。 Prismフレームワークによって提供されるDelegateCommandクラスに飛び込むまで、私は1日壁に頭をぶつけました。私はコードを持っていませんが、解決策を少し投稿することができます。

別の方法は、他のRelayCommandフレームワークの1つを使用することです。

編集
コードを再投稿するのではなく、解決策を提供する他のSO質問があります:

そして、ケントB.には良い記事があります: MVVMインフラストラクチャ:DelegateCommand

0
Metro Smurf