web-dev-qa-db-ja.com

PropertyChanged後にICommand CanExecuteがトリガーされませんか?

そのようなコマンドにバインドされたボタンを示すWPFアプリケーションを取得しました:

<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">

コマンドは次のように定義されます:

public ICommand TestrunStartCommand
{
    get { return new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress); }
}

public bool IsTestrunInProgress
{
    get{
        return _isTestrunInProgress;
    }
    set{
        _isTestrunInProgress = value;
        RaisePropertyChanged(IsTestrunInProgressPropertyName);
    }
}   

問題は、IsTestrunInProgressをfalseに設定した直後にボタンが有効にならず、アプリケーションウィンドウ内をクリックした後にのみ有効になることです

この動作を理解し、これを修正する方法を教えてください。

さらに読む:wpfコマンドパターン-クエリはいつ実行可能か

37
nabulke

ICommandインターフェースはイベントを公開します _ICommand.CanExecuteChanged_ これは、コマンド駆動型UIコンポーネントのIsEnabled状態を再決定するタイミングをUIに通知するために使用されます。

使用しているRelayCommandの実装に応じて、このイベントを発生させる必要があります。多くの実装は、UIを強制的に更新するために呼び出すことができるRelayCommand.RaiseCanExecuteChanged()などのメソッドを公開します。

RelayCommandの一部の実装では、 _CommandManager.RequerySuggested_ を使用します。この場合、強制的に CommandManager.InvalidateRequerySuggested() を呼び出す必要があります。更新するUI。

要するに、プロパティセッターからこれらのメソッドのいずれかを呼び出す必要があります。

更新

アクティブフォーカスが変更されるときにボタンの状態が決定されるので、CommandManagerが使用されていると思います。そのため、プロパティのセッターで、バッキングフィールドを割り当てた後、 CommandManager.InvalidateRequerySuggested() を呼び出します。

更新2

RelayCommand実装は、MVVMライトツールキットからのものです。 WPF/.NETから消費されると、実装はCommandManagerから公開されたメソッドとイベントをラップします。これは、これらのコマンドがほとんどの状況(UIが変更されるか、フォーカスされた要素が変更される)で自動的に機能することを意味します。ただし、このようないくつかのケースでは、コマンドを手動で強制的に再クエリする必要があります。このライブラリを使用してこれを行う適切な方法は、RelayCommandRaiseCanExecuteChanged()メソッドを呼び出すことです。

48
Lukazoid

これはとても重要で見逃しがちです。@ Samirがコメントで言ったことを繰り返します。 Laurent Bugnion氏は blog で次のように書いています。

ただし、WPF 4およびWPF 4.5には問題があります。MVVMLightをV5にアップグレードすると、CommandManagerが機能しなくなります。観察するのは、RelayCommandのCanExecuteデリゲートがfalseを返すと、UI要素(ボタンなど)の無効化/有効化が停止することです。

急いでいる場合、ここに修正があります:RelayCommandを使用するクラスでは、次の行を置き換えます:

using GalaSoft.MvvmLight.Command;

with:

using GalaSoft.MvvmLight.CommandWpf;
47
Heliac

CommandManager.InvalidateRequerySuggested で試すことができます。

とにかく、これは過去に時々私を助けませんでした。私にとって最良の解決策は、ブールプロパティをButton.IsEnabled依存プロパティにバインドすることでした。

あなたの場合のようなもの

IsEnabled={Binding IsTestrunInProgress}
4
Klaus78

問題は、ICommandプロパティTestrunStartCommandがアクセスされるたびに新しいコマンドオブジェクトを常に返すことです。

簡単な修正方法は、ICommandオブジェクトを一度作成して、何度も使用することです。

private ICommand _testRunCommand = null;
public ICommand TestrunStartCommand
{
    get 
    { 
        return _testRunCommand ?? (_testRunCommand = new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress)); 
    }
}

これは非常に簡単な修正であり、私にとってはうまくいきました。

1
Amit Kumar