我在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是一个绑定到视图上的Telerik繁忙指示器的bool,因此用户可以看到发生了什么。
运行此操作时,在您单击窗口某处之前,不会正确启用或禁用撤消按钮。我在整个地方添加了Debug.WriteLine语句,我可以看到在任务回调结束时没有调用UndoCommandCanExecute方法。
我尝试在任务回调结束时添加以下两行,但它没有帮助......
CommandManager.InvalidateRequerySuggested();
UndoCommand.RaiseCanExecuteChanged();
如果我删除Thread.Sleep,那么它工作正常,这听起来像是问题的延迟。此方法在任务结束之前结束,因此UndoCommandCanExecute方法不会触发。当我调用它的RaiseCanExecuteChanged方法或CommandManager时,我不明白为什么它不会触发。
我尝试在启动线程后立即添加t.Wait(),但忙碌指示器在服务调用结束后才显示,否定了整个目的。
有任何想法如何解决这个问题?我希望保持异步调用,因为这允许我使用繁忙的指示符,这是整个大型应用程序的标准,并且还向用户提供一些反馈,说明发生了什么。
我顺便使用C#4,所以C#5的新异步功能对我来说是不可用的,即使它们可以解决问题。
答案 0 :(得分:1)
我有同样的问题,我能够以两种不同的方式纠正它,不确定哪种是“最佳”。
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也可以使用