我遇到了与Tasks和async / await关键词的混淆。我知道你不应该混合异步和阻塞代码。或者至少我对混合它们的解释是:
不要打电话来阻止来自非异步方法的API。所以这就是我的问题。
我正在尝试等待一个方法,然后相应地更新UI。问题是等待异步方法()调用的唯一方法是来自内部和异步方法()。
以下是一个例子:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
await RunValidation(options, cancellationTokenSource.Token);
Completed = true;
}));
}
}
此代码正确运行该方法并等待。问题是我什么时候回来。出于某种原因,在设置“完成”标志时,不会切换依赖于此标志的按钮。如果我评论等待代码,则按钮正确切换。所以假设我没有返回UI线程,所以我尝试使用这个代码:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
{
Completed = false;
var cancellationTokenSource = new CancellationTokenSource();
var context = TaskScheduler.FromCurrentSynchronizationContext();
await RunValidation(options, cancellationTokenSource.Token).ContinueWith(t => Completed = true, context);
//Completed = true;
}));
}
}
这是RunValidation()方法:
private async Task RunValidation(Options options, CancellationToken token)
{
await _someService.SomAsyncMethod(options, token));
}
如果您注意到,ExecuteCommand在传递给命令的(options)参数之前有一个异步关键字。如果我删除了异步关键字,那么我必须修改对RunValidation()方法的调用。我仍然需要等待,所以这就是我所做的:
private RelayCommand<Options> _executeCommand;
public RelayCommand<Options> ExecuteCommand
{
get
{
return _executeCommand ?? (_executeCommand = new RelayCommand<Options>((options) =>
{
Completed = false;
var context = TaskScheduler.FromCurrentSynchronizationContext();
var cancellationTokenSource = new CancellationTokenSource();
Task.Run(async () => await RunValidation(options, cancellationTokenSource.Token));
Completed = true;
}));
}
}
此代码的问题在于它没有等待。所以我不知所措。
任何人都可以为我解释这个问题。我已经花了2天以上的时间,我还在这里。
谢谢, 添
以下是命令按钮的绑定。
private readonly Independent<bool> _completed = new Independent<bool>(true);
public bool Completed
{
get { return _completed; }
set { _completed.Value = value; }
}
private ICommand _doneCommand;
public ICommand DoneCommand
{
get
{
return _doneCommand ?? (_doneCommand = MakeCommand.When(() => Completed).Do(() =>
{
DoSomething();
}));
}
}
private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ??
(_cancelCommand = MakeCommand.When(() => !Completed).Do(() => DoSomthingElse()));
}
}
我正在使用Michael Perry的UpdateControls库中的MakeCommand对象。它们包含依赖性跟踪,当完成属性更改时,它会引发CanExecuteChange事件。
答案 0 :(得分:0)
您的第一个代码是正确的。很可能您的Completed
属性的实施不正确。您的视图模型对象应该实现INotifyPropertyChanged。最简单的方法是使用提供功能的基类。 ReactiveUI是我一直使用的nuget包。用法就像
public class MyObject : ReactiveObject {
private bool _Completed;
public bool Completed {
get => _Completed;
set => this.RaiseAndSetIfChanged(ref _Completed, value);
}
}
这将确保在更改属性时将通知引发到UI。
如果您希望它更具魔力,可以使用ReactiveUI.Fody,然后您的代码将缩减为
public class MyObject : ReactiveObject {
[Reactive]
public bool Completed { get; set;}
}
答案 1 :(得分:0)
所以问题实际上是第三方图书馆。我用它来为2个按钮提供依赖性跟踪。因此,当完整标志发生更改时,它会为两个按钮引发CanExecuteChange事件,而无需编写代码来执行此操作。不幸的是,它在引入async / await调用后停止了工作。我用RelayCommands替换了2个MakeCommands并自己引发了事件,一切正常。
感谢大家的回复。
添