2009-04-23 12 views
64

CanExecuteをカスタムコマンド(Josh SmithのRelayCommand)に呼び出す方法を知っている人はいますか?WPFコマンドをリフレッシュ

通常、UIで対話が発生するたびにCanExecuteが呼び出されます。何かをクリックすると、私のコマンドが更新されます。

私は、CanExecuteの条件が裏返しのタイマーによってオン/オフになっている状況があります。これはユーザーとのやりとりによって駆動されないため、ユーザーがUIと対話するまでCanExecuteは呼び出されません。最終的に私のButtonはユーザーがそれをクリックするまで有効/無効のままです。クリック後、正しく更新されます。ときにはButtonが有効になっているように見えることがありますが、ユーザーがクリックすると、発砲する代わりに無効に変わります。

CanExecuteに影響するプロパティがタイマーによって変更された場合、どのようにコードを更新することができますか? CanExecuteに影響を与えるプロパティでPropertyChangedINotifyPropertyChanged)を実行しようとしましたが、それは役に立ちませんでした。

例XAML:

<Button Content="Button" Command="{Binding Cmd}"/> 

の背後にあるコード例:

private ICommand m_cmd; 
public ICommand Cmd 
{ 
    if (m_cmd == null) 
     m_cmd = new RelayCommand(
      (param) => Process(), 
      (param) => EnableButton); 

    return m_cmd; 
} 

// Gets updated from a timer (not direct user interaction) 
public bool EnableButton { get; set; } 
+0

コマンドに対してINotifyPropertyChangedを呼び出そうとしましたか?あなたはコマンドのフィールドを持つ必要はありません、ちょうど新しいものを毎回返します。この組み合わせはうまくいくはずです。または、強制が必要な場合にのみ新しいコマンドを作成します。 – egaga

答えて

98
+1

これはViewModelクラスから呼び出すことをお勧めしますか? –

+2

必ずしもあなたのクラスをテストするのが難しいかもしれません。試してみて、必要に応じてサービスに移動してください。もう一つの選択肢はRelayCommandにメソッドを追加して、そのコマンドのためだけにCanExecuteChangedを呼び出すことができるようにすることです(CommandManager.InvalidRequerySuggestedは多少の過剰なコマンドを無効にします)。 –

+23

興味深い...それは動作しますが、UIスレッドで呼び出される必要があります。私は驚いていない。 –

27

私は長い時間前にCommandManager.InvalidateRequerySuggested()を知っていたし、それを使用しますが、それ時々私のために働いていなかった。私は最終的になぜこれが事実であるかを考え出しました!他のアクションと同じようにスローされませんが、メインスレッドで呼び出す必要があります。

バックグラウンドスレッドで呼び出すと動作するように見えますが、UIが無効のままになることがあります。私はこれが誰かを助けてくれることを願っています。

4

ヒントの方に感謝します。再クエリをトリガするためにバックグラウンドスレッドで

syncCtx = SynchronizationContext.Current; 

、:コンストラクタで

private SynchronizationContext syncCtx; // member variable 

:ここではBGスレッドからUIスレッドにその呼び出しをマーシャリングする方法についてのコードのビットです:

syncCtx.Post(delegate { CommandManager.InvalidateRequerySuggested(); }, null); 

希望します。

<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/> 

をして、あなたのViewModelでこのプロパティを実装: -

マイケル

+3

Dispatcherを呼び出すほうがよいようです。 BeginInvoke() –

+0

こんにちはジョシュ。多分それは良いでしょう。内部的には、Dispatcher.BeginInvoke()はSynchronizationContextに委譲するSynchronizationContextSwitcherクラスを使用します。 –

14

それを回避するには、プロパティにIsEnabledを結合されます。また、UnitTestingがコマンドではなくプロパティで作業して、特定の時点でコマンドを実行できるかどうかを確認するのが少し楽になります。

私は、個人的には、より便利です。

+0

IsEnabledをどのようにリフレッシュしますか? – visc

+0

これはすべてを無効にしないでください。これはより良い、より簡単な解決策です。 – Asheh

5

おそらくこのバリアントは、あなたに合う:

public interface IRelayCommand : ICommand 
{ 
    void UpdateCanExecuteState(); 
} 

実装:

public class RelayCommand : IRelayCommand 
{ 
    public event EventHandler CanExecuteChanged; 


    readonly Predicate<Object> _canExecute = null; 
    readonly Action<Object> _executeAction = null; 

    public RelayCommand(Action<object> executeAction,Predicate<Object> canExecute = null) 
    { 
     _canExecute = canExecute; 
     _executeAction = executeAction; 
    } 


    public bool CanExecute(object parameter) 
    { 
     if (_canExecute != null) 
      return _canExecute(parameter); 
     return true; 
    } 

    public void UpdateCanExecuteState() 
    { 
     if (CanExecuteChanged != null) 
      CanExecuteChanged(this, new EventArgs()); 
    } 



    public void Execute(object parameter) 
    { 
     if (_executeAction != null) 
      _executeAction(parameter); 
     UpdateCanExecuteState(); 
    } 
} 

の簡単な使い方:

public IRelayCommand EditCommand { get; protected set; } 
... 
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted); 

protected override bool CanEditCommandExecuted(object obj) 
    { 
     return SelectedItem != null ; 
    } 

    protected override void EditCommandExecuted(object obj) 
    { 
     // Do something 
    } 

    ... 

    public TEntity SelectedItem 
    { 
     get { return _selectedItem; } 
     set 
     { 
      _selectedItem = value; 

      //Refresh can execute 
      EditCommand.UpdateCanExecuteState(); 

      RaisePropertyChanged(() => SelectedItem); 
     } 
    } 

XAML:

<Button Content="Edit" Command="{Binding EditCommand}"/> 
+1

ハンドラへの強い参照を作成してメモリリークを引き起こすため、理想的ではありません。 executeActionが新しいワーカースレッドを開始すると、 –

+0

は動作しません – Steve

関連する問題