2

我在 WPF4 项目中使用 MVVMLight,并设置了 ApplyCommand 和 UndoCommand,它们绑定到视图上的按钮。所有标准的东西,做了很多次。当用户单击按钮时,将调用 WCF 服务方法,并更新局部变量。UndoCommandCanExecute 方法使用此局部变量来确定 UndoCommand 是否可以运行。

如果我同步进行 WCF 服务调用,这可以正常工作。但是,如果我使用异步调用,则不会触发 CanExecute 方法,因此无法正确启用/禁用撤消按钮。

这是视图模型上由两个命令调用的私有方法的简化版本。在实际代码中,传入参数,在服务调用中使用。

  private void CallServiceMethod() {
    IsBusy = true;
    Task t = new Task(() => {
      Thread.Sleep(2000); // would really call a WCF service
      SetUndoMessage();
      IsBusy = false;
    });
    t.Start();
  }

如前所述,为简单起见,WCF 服务调用已替换为对 Thread.Sleep 的调用。SetUndoMessage() 方法设置 UndoCommandCanExecute 方法使用的局部变量。IsBusy 是一个 bool,绑定到视图上的 Telerik 忙碌指示器,因此用户可以看到正在发生的事情。

运行此程序时,在您单击窗口上的某个位置之前,不会正确启用或禁用撤消按钮。我在所有地方都添加了 Debug.WriteLine 语句,我可以看到在任务回调结束时没有调用 UndoCommandCanExecute 方法。

我尝试在任务回调的末尾添加以下两行,但它没有帮助......

    CommandManager.InvalidateRequerySuggested();
    UndoCommand.RaiseCanExecuteChanged();

如果我删除 Thread.Sleep,那么它工作正常,这听起来像是延迟问题。此方法在任务结束之前结束,因此 UndoCommandCanExecute 方法不会触发。我不明白为什么当我调用它的 RaiseCanExecuteChanged 方法或 CommandManager 时它不会触发。

我尝试在启动线程后立即添加 t.Wait() ,但是直到服务调用结束后忙指示符才出现,否定了整个目的。

任何人有任何想法如何解决这个问题?我想保留异步调用,因为这样我就可以使用忙碌指示器,这是整个大型应用程序的标准配置,并且还可以向用户提供一些正在发生的事情的反馈。

顺便说一句,我一直在使用 C# 4,因此我无法使用 C# 5 的新异步功能,即使它们可以解决问题。

4

1 回答 1

2

我有同样的问题,我能够以两种不同的方式纠正它,不确定哪个是“最好的”。

1)在我的任务中,我放了:

App.Current.Dispatcher.BeginInvoke((Action)delegate()
{
    yourCommand.RaiseCanExecuteChanged();
});

我真的不喜欢在多个地方调用 RaiseCanExecuteChanged 的​​想法,所以我最终使用了这个:

2) 一世。我添加到构造函数:

this.PropertyChanged+=my_PropertyChanged;

ii.

private void me_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "whateverPropertyNamethatcontrolsit":
            ((RelayCommand)FirstCommand).RaiseCanExecuteChanged();
            break;
    }
}

iii. 任务内部:

App.Current.Dispatcher.BeginInvoke((Action)delegate()
{
    whateverProperty=something;
});

请注意,我将 MVVMLight 与 RelayCommand 一起使用,但 DelegateCommand 而不是 RelayCommand 也可以。

于 2013-11-15T15:24:52.737 回答