本文介绍了WPF ICommand CanExecute():RaiseCanExecuteChanged()还是通过DispatchTimer自动处理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试确定使ICommands的CanExecute()反映在UI中的最佳方法。

I'm trying to determine the best way to cause ICommands' CanExecute() to be reflected in the UI.

我知道 Dispatcher 是处理UI绘图的WPF(引擎?),默认情况下,Dispatcher会在实例化以及活动用户界面(单击UI或键盘输入)时评估ICommands的CanExecute()方法。

I understand that the Dispatcher is the WPF (engine?) that handles UI drawing, and that by default, the Dispatcher evaluates ICommands' CanExecute() method upon instantiation as well as active user interface (clicking the UI, or keyboard input.)

显然,当任何给定的ICommand上的CanExecute()更改但没有提供鼠标或键盘输入时,这是一个问题-因此UI不会更改以反映更改ICommand CanExecute()状态。

Obviously, this is a problem when CanExecute() on any given ICommand changes, but neither mouse nor keyboard input is provided--and so the UI does not change to reflect the change in ICommand CanExecute() state.

似乎有两个解决方案,都包括调用System.Windows.Input.CommandManager.InvalidateRequerySuggested()。

这指示分派器重新评估每个ICommand的CanExecute()并相应地更新UI。我也了解这可能与性能相关,但是,只有当一个ICommands(1000+?)过多或正在使用他们不应该使用的CanExecute()方法执行工作时,这才似乎是一个问题。

This instructs the Dispatcher to re-evaluate each ICommand's CanExecute() and update the UI accordingly. I also understand that this can have performance-related issues, but that only seems to be an issue if one has a lot of ICommands (1000+?) or is performing work in their CanExecute() methods that they shouldn't be (for example, a network operation.)

假定一个人具有构造良好的CanExecute()方法,并且鉴于解决方案是调用InvalidateRequerySuggested,这是我的两种方法我们发现这样做:

Assuming that one has well-constructed CanExecute() methods, and given that the solution is to call InvalidateRequerySuggested, here are two ways I've found to do so:


  1. 在ICommand接口解决方案上实施 RaiseCanExecuteChanged()方法。

是否使用从ICommand继承的DelegateCommand或RelayCommand(或其他实现),它们都会添加 RaiseCanExecuteChanged()公共方法只需调用上面的InvalidateRequerySuggested,然后Dispatcher重新评估所有ICommands并相应地更新UI。

Whether one is using DelegateCommand or RelayCommand (or some other implementation) inheriting from ICommand, they add a "RaiseCanExecuteChanged()" public method which simply calls the above InvalidateRequerySuggested, and the Dispatcher re-evaluates all ICommands and updates the UI accordingly.

使用此方法,可能应该以以下方式调用它: / p>

Using this method, one should probably invoke it the following way:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Normal,
    (System.Action)(() =>
    {
        System.Windows.Input.CommandManager.InvalidateRequerySuggested();
    }));

(据我所知,之所以应使用此原因,是因为它告诉了分派器在主UI线程上调用InvalidateRequerySuggested(),并且取决于一个调用 RaiseCanExecuteChanged()的位置,它们可能不在UI线程上,因此分派器将尝试更新该线程(而不是主UI)

(The reason why this should be used, as far as I can tell, is because this tells the Dispatcher to invoke the InvalidateRequerySuggested() on the main UI thread--and depending on where one calls "RaiseCanExecuteChanged()", they may not on the UI thread, and thus the Dispatcher would try to update that thread (instead of the main UI thread), not resulting in the controls/UI being updated as expected.)


  1. 在开始时执行DispatcherTimer您的应用程序并在计时器上运行,使其定期调用InvalidateRequerySuggested。

此解决方案创建一个DispatcherTimer(在后台线程上运行)按设置的时间间隔,然后在该时间间隔调用InvalidateRequerySuggested(),刷新ICommands。 DispatcherTimer的本质是它运行在Dispatcher线程(UI线程)上,因此不需要上述包装的调用。

This solution creates a DispatcherTimer (running on a background thread) at a set interval, which then calls InvalidateRequerySuggested() on that interval, refreshing ICommands. The nature of a DispatcherTimer is that it runs on the Dispatcher thread (UI thread), so the above wrapped call isn't necessary.

例如,可以添加一个转到其App.xaml.cs:

For example, one could add this to their App.xaml.cs here:

    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.
    }

此解决方案导致InvalidateRequerySuggested()在给定的计时器上重新评估(显示

This solution causes InvalidateRequerySuggested() to re-evaluate on the given timer (presented here every quarter of a second) and automatically update the UI as appropriate.

问题/想法

我喜欢DispatcherTimer自动运行并按设定的时间间隔重新评估的想法。但是,该间隔必须很小,否则对用户来说,ICommand的CanExecute()更改和UI更新之间的延迟可能会很大。 (例如,如果DispatchTimer以10秒的间隔运行,并且您的应用程序中发生某些事情,导致按钮的CanExecute()更改为False,则可能需要长达10秒钟的时间,UI才会相应更新。)

I like the idea of a DispatcherTimer running automatically and re-evaluating on a set interval. However, that interval must be small, otherwise the lag between an ICommand's CanExecute() changing and the UI updating might be significant to the user. (For example, if the DispatchTimer is running on a 10 second interval, and something happens in your application to cause a button's CanExecute() to change to False, it might take up to 10 seconds before the UI updates accordingly.)

我不喜欢这种解决方案,因为它感觉像是在重新评估ICommands的数量。。但是,以这种方式自动更新UI无需调用 RaiseCanExecuteChanged (),并保存样板。

I dislike this solution because it feels like it's re-evaluating ICommands a lot. However, automatically updating the UI this way removes the need to call "RaiseCanExecuteChanged()" manually, and saves boilerplate.

INotifyPropertyChanged 与RaiseCanExecuteChanged()类似,但是使用NotifyPropertyChanged(string propertyName)处理仅更新指定的属性。但是,可以将 null 传递给NotifyPropertyChanged,这将导致(调度程序?)重新评估所有属性,实际上是刷新它们。

INotifyPropertyChanged works similarly to RaiseCanExecuteChanged(), but instead, uses NotifyPropertyChanged(string propertyName) to handle updating only the specified Property. One can pass null to NotifyPropertyChanged, though, which causes the (Dispatcher?) to re-evaluate all Properties, in effect refreshing them.


  1. 社区认为实现InvalidateRequerySuggested()的最佳方法是什么?通过RaiseCanExecuteChanged()还是通过DispatcherTimer?

  2. 是否可以消除调用 NotifyPropertyChanged()的需要,而在上述DispatcherTimer中不仅可以调用 InvalidateRequerySuggested(),还可以调用 NotifyPropertyChanged (空),同时刷新INotifyPropertyChanged和ICommands?


推荐答案

回答您的问题:显然,当给定的ICommand上的CanExecute()更改但没有提供鼠标或键盘输入时,这是一个问题-因此UI不会更改以反映ICommand CanExecute( )状态。:

In answer to your question: "Obviously, this is a problem when CanExecute() on any given ICommand changes, but neither mouse nor keyboard input is provided--and so the UI does not change to reflect the change in ICommand CanExecute() state.":

通常,您将按钮的 IsEnabled 状态绑定到ViewModel中的属性。这意味着您可以手动启用或禁用该按钮。

Normally, you would bind the IsEnabled state of the button to a property in the ViewModel. This means you could manually enable or disable the button.

对于 IsEnabled CanExecute()是有效的语法糖c>属性。

The CanExecute() is effectively syntactic sugar for the IsEnabled property.

现在我们可以控制按钮的 IsEnabled 状态,我们可以在此之上构建任何东西:可以使用其他按钮控制其状态,或订阅
RX(反应性扩展)流等。

Now that we can control the IsEnabled state of the button, we can build anything on top of that: we can control its state using other buttons, or subscribe to a RX (Reactive Extensions) stream, etc.

这篇关于WPF ICommand CanExecute():RaiseCanExecuteChanged()还是通过DispatchTimer自动处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-15 08:35