カスタムコマンド(Josh Smithの CanExecute
)でRelayCommand
を強制的に呼び出す方法を知っている人はいますか?
通常、CanExecute
は、UIで対話が発生するたびに呼び出されます。何かをクリックすると、コマンドが更新されます。
舞台裏でタイマーによってCanExecute
の条件がオン/オフになる状況があります。これはユーザーの操作ではないため、ユーザーがUIを操作するまでCanExecute
は呼び出されません。最終結果は、ユーザーがクリックするまでButton
が有効/無効のままになります。クリック後、正しく更新されます。 Button
は有効になっているように見える場合がありますが、ユーザーがクリックすると、起動ではなく無効に変わります。
タイマーがCanExecute
に影響するプロパティを変更したときに、コードの更新を強制するにはどうすればよいですか? PropertyChanged
に影響するプロパティでINotifyPropertyChanged
(CanExecute
)を実行しようとしましたが、助けにはなりませんでした。
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; }
私はずっと前にCommandManager.InvalidateRequerySuggested()を知っていて、それを使用しましたが、それは時々私にとってはうまくいきませんでした。私はついにこれが事実である理由を見つけました!他のアクションのようにスローされない場合でも、メインスレッドで呼び出す必要があります。
バックグラウンドスレッドで呼び出すと動作するように見えますが、UIが無効のままになることもあります。これが誰かの助けになり、無駄にした時間を節約できることを本当に願っています。
そのための回避策は、IsEnabled
をプロパティにバインドすることです。
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
次に、このプロパティをViewModelに実装します。これにより、UnitTestingがコマンドよりもプロパティを操作しやすくなり、特定の時点でコマンドを実行できるかどうかを確認できます。
個人的には、より便利だと思います。
おそらくこのバリアントはあなたに合っています:
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}"/>
ヒントをありがとう。 BGスレッドからUIスレッドへの呼び出しをマーシャリングする方法に関するコードを次に示します。
private SynchronizationContext syncCtx; // member variable
コンストラクター内:
syncCtx = SynchronizationContext.Current;
バックグラウンドスレッドで、再クエリをトリガーするには:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
お役に立てば幸いです。
-マイケル
単一のGalaSoft.MvvmLight.CommandWpf.RelayCommandのみを更新するには、使用できます
mycommand.RaiseCanExecuteChanged();
そして私のために拡張メソッドを作成しました:
public static class ExtensionMethods
{
public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
}