我有一个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后,该按钮不会立即启用,但只有在我单击应用程序窗口内部后才会启用。
你能帮我理解这个行为并告诉我如何解决这个问题吗?
ICommand
接口公开了一个事件ICommand.CanExecuteChanged
,用于通知UI何时重新确定命令驱动的UI组件的IsEnabled
状态。
根据您使用的RelayCommand
的实施情况,您可能需要提出此事件;许多实现都公开了一个方法,例如RelayCommand.RaiseCanExecuteChanged()
,您可以调用该方法来强制UI刷新。
RelayCommand
的一些实现使用CommandManager.RequerySuggested
,在这种情况下,您需要调用CommandManager.InvalidateRequerySuggested()
来强制UI刷新。
简而言之,您需要从属性设置器中调用其中一种方法。
更新
由于当活动焦点正在改变时确定按钮的状态,我相信正在使用CommandManager
。因此,在您的属性的setter中,在分配支持字段后,调用CommandManager.InvalidateRequerySuggested()
。
更新2
RelayCommand
实现来自MVVM light工具包。从WPF / .NET使用时,实现将包装从CommandManager
公开的方法和事件。这意味着这些命令在大多数情况下(UI被更改,或者焦点元素被更改)自动运行。但在少数情况下,例如这个,你需要手动强制命令重新查询。使用这个库执行此操作的正确方法是在RaiseCanExecuteChanged()
上调用RelayCommand
方法。
这是非常重要且容易错过,我在重复@Samir在评论中所说的内容。 Laurent Bugnion先生在他的blog写道:
但是,在WPF 4和WPF 4.5中,有一个问题:在将MVVM Light升级到V5后,CommandManager将停止工作。您将观察到当RelayCommand的CanExecute委托返回false时,您的UI元素(按钮等)将停止被禁用/启用。
如果你赶时间,这里有修复:在使用RelayCommand的任何类中,替换行说:
using GalaSoft.MvvmLight.Command;
有:
using GalaSoft.MvvmLight.CommandWpf;
你可以试试CommandManager.InvalidateRequerySuggested。
无论如何,这在过去有时对我没有帮助。对我来说,最好的解决方案是将boolean属性绑定到Button.IsEnabled
依赖属性。
在你的情况下像
IsEnabled={Binding IsTestrunInProgress}
问题是,ICommand Property TestrunStartCommand在访问时始终返回一个新的命令对象。
一个简单的解决方法是创建一次ICommand对象并一次又一次地使用它。
private ICommand _testRunCommand = null;
public ICommand TestrunStartCommand
{
get
{
return _testRunCommand ?? (_testRunCommand = new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress));
}
}
这是一个非常简单的修复,它对我有用。