2009-04-23 106 views
64

有谁知道我可以如何强制CanExecute调用一个自定义命令(约什史密斯的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?你不需要为Command指定一个字段,每次只需要返回一个新字段。这种组合应该可行。或者只在需要强制的情况下创建新的命令。 – egaga 2013-02-15 17:03:52

回答

98
+1

你是否建议从ViewModel类调用它? – 2009-04-23 19:10:59

+2

不一定,因为这可能会让你的课难以测试。尝试一下,并在必要时将其转移到服务中。另一种方法是在RelayCommand中添加一个方法,该方法允许您为该命令引发CanExecuteChanged(CommandManager.InvalidRequerySuggested使所有命令无效,这有点矫枉过正)。 – 2009-04-23 19:15:49

+23

有趣......它的工作原理,但它必须在UI线程上调用。我不惊讶。 – 2009-04-23 19:16:03

27

我知道CommandManager.InvalidateRequerySuggested()的很久以前,并用它,但它有时候我不适合我。我终于明白为什么会是这种情况!尽管它不会像其他一些操作一样,但您必须在主线程中调用它。

在后台线程上调用它将显示工作,但有时会禁用UI。我真的希望这能帮助别人,并为他们节省我浪费的时间。

4

谢谢你们的提示。下面是关于如何在呼叫元帅从BG线程UI线程一些代码:

private SynchronizationContext syncCtx; // member variable 

在构造函数中:

syncCtx = SynchronizationContext.Current; 

在后台线程,触发重新查询:

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

希望有所帮助。

迈克尔 -

14

,一种在解决方法是结合IsEnabled一个属性:

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

,然后实现在您的视图模型此属性。这也使得UnitTesting更容易处理属性而不是命令,以查看命令是否可以在某个时间点执行。

我个人认为它更方便。

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}"/>