C# WPF ICommand CanExecute():RaiseCanExecuteChanged()还是通过DispatchTimer自动处理?
我正在尝试确定在UI中反映ICommands的CanExecute()的最佳方式 我知道调度程序是处理UI绘图的WPF(引擎?),默认情况下,调度程序在实例化ICommands的CanExecute()方法以及活动用户界面(单击UI或键盘输入)时对其进行评估 显然,当任何给定ICommand上的CanExecute()发生更改,但既不提供鼠标也不提供键盘输入时,这是一个问题,因此UI不会更改以反映ICommand CanExecute()状态的更改 这个问题似乎有两种解决方案,都包括调用System.Windows.Input.CommandManager.InvalidateRequestSuggested()。 这指示调度器重新评估每个ICommand的CanExecute(),并相应地更新UI。我也知道这可能会有与性能相关的问题,但只有当一个人有很多icommand(1000+?)或正在CanExecute()方法中执行不应该执行的工作(例如,网络操作)时,这才似乎是一个问题 假设有一个构造良好的CanExecute()方法,并且解决方案是调用InvalidateRequestySuggested,我发现有两种方法可以做到这一点:C# WPF ICommand CanExecute():RaiseCanExecuteChanged()还是通过DispatchTimer自动处理?,c#,wpf,inotifypropertychanged,dispatcher,icommand,C#,Wpf,Inotifypropertychanged,Dispatcher,Icommand,我正在尝试确定在UI中反映ICommands的CanExecute()的最佳方式 我知道调度程序是处理UI绘图的WPF(引擎?),默认情况下,调度程序在实例化ICommands的CanExecute()方法以及活动用户界面(单击UI或键盘输入)时对其进行评估 显然,当任何给定ICommand上的CanExecute()发生更改,但既不提供鼠标也不提供键盘输入时,这是一个问题,因此UI不会更改以反映ICommand CanExecute()状态的更改 这个问题似乎有两种解决方案,都包括调用Syst
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(System.Action)(() =>
{
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}));
(据我所知,之所以应该使用它,是因为它告诉调度程序在主UI线程上调用InvalidateRequestSuggested()——并且根据调用“RaiseCanceTechChanged()”的位置,它们可能不在UI线程上,因此调度程序将尝试更新该线程(而不是主UI线程),不会导致控件/UI按预期更新。)
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer()
{
Interval = new TimeSpan(0, 0, 0, 0, 25),
IsEnabled = true
};
dt.Tick += delegate(object sender, EventArgs be)
{
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
};
dt.Start();
// ... Other startup logic in your app here.
}
此解决方案会导致InvalidateRequestSuggested()在给定计时器上重新计算(此处每四分之一秒显示一次),并根据需要自动更新UI
问题/想法
我喜欢调度员自动运行并按设定的时间间隔重新评估的想法。但是,该间隔必须很小,否则ICommand的CanExecute()更改和UI更新之间的延迟可能对用户很重要。(例如,如果DispatchTimer以10秒的间隔运行,并且应用程序中发生了一些事情,导致按钮的CanExecute()更改为False,则在UI相应更新之前可能需要10秒。)
我不喜欢这个解决方案,因为它感觉像是在重新评估ICommands。但是,通过这种方式自动更新UI,就不需要手动调用“RaiseCanExecuteChanged()”并保存样板文件
INotifyPropertyChanged的工作原理类似于RaiseCanceTechChanged(),但使用NotifyPropertyChanged(string propertyName)仅处理指定属性的更新。但是,可以将null传递给NotifyPropertyChanged,这会导致(Dispatcher?)重新计算所有属性,实际上是刷新它们
在回答您的问题时:“显然,当任何给定ICommand上的CanExecute()发生更改时,这是一个问题,但既不提供鼠标输入也不提供键盘输入,因此UI不会更改以反映ICommand CanExecute()状态的更改。” 通常,您会将按钮的
IsEnabled
状态绑定到ViewModel中的属性。这意味着您可以手动启用或禁用该按钮
CanExecute()
是IsEnabled
属性的有效语法糖类
现在我们可以控制按钮的IsEnabled
状态,我们可以在此基础上构建任何东西:我们可以使用其他按钮控制其状态,或者订阅
RX(反应式扩展)流等。假设有一个函数提供一个布尔值,
CanExecute
调用,那么是什么更新了这个函数?@Contango一个典型的例子是Josh Smith的RelayCommand实现,您可以在这里查看:注意此实现如何接受操作和谓词作为ICommand的CanExecute()和Execute()的参数。