在WPF控件(例如网格)中,我们通常可以设置一个布尔属性以显示控件正在忙于加载数据,在UI中这将导致“正在加载...”指示符。
使用async
方法时,我们只需要确保在调用该方法之前将IsBusy = "true"
旋转,将IsBusy="false"
之后的await
旋转即可。
但是,如果我可以在第一次调用完成时多次调用网格加载方法,那么即使在第二次调用正在进行时,它也会关闭繁忙指示器。
有什么办法解决这个问题?我可以设置一个用于存储请求数的全局计数器,并根据此全局变量的计数来设置指标的状态,但是这是一种肮脏的方法,如果我的代码中有多个异步事件,将无法缩放。
示例场景
在下图中,我可以搜索学生的姓名,对于我的服务,每个名称都会返回详细信息(标记等)并将其显示在第二个网格中。
我想在第二个网格正在等待数据时显示繁忙指示器(否则用户可能不知道程序是否在做任何事情)。
输入名称时,将调用以下方法:
想象GetStudentResults
花费5秒(每次通话)。我在0秒输入名字,然后在3秒输入另一个名字。现在,在5秒时,第一个呼叫返回,并且它关闭了忙音指示器,而第二个名称的详细信息未检索到。这就是我要避免的事情。
private async void SearchName(string name)
{
ResultDisplayGrid.IsBusy = true;
await GetStudentResults();
ResultDisplayGrid.IsBusy = false;
}
答案 0 :(得分:1)
请尝试将您的异步调用包装在try-finally块中,完成所有操作后,它将调用finally将IsBusy标志设置为false。
private async void SearchName(string name)
{
ResultDisplayGrid.IsBusy = true;
try{
await GetStudentResults();
}
finally{
ResultDisplayGrid.IsBusy = false;
}
}
答案 1 :(得分:1)
自从最近发表评论以来,就一直在考虑这一点,因此它需要一个涉及适当任务管理的更复杂的解决方案,并且在协助他人时这超出了我的舒适范围。
我认为最快,最简单的方法是一旦搜索开始就阻止用户与文本框或GUI进行交互,从而防止在前一个搜索完成之前进行其他搜索。当然,这意味着用户将需要等待每个搜索完成才能开始下一个搜索。
我的下一个方法是存储GetStudentResults任务并使用CancellationToken。例如SearchName可能变为:
private CancellationTokenSource ctsSearch;
private Task tSearch;
private async void SearchName(string name)
{
if(ctsSearch != null)
{
ctsSearch.Cancel();
if(tSearch != null)
await tSearch;
}
ctsSearch = new CancellationTokenSource();
ResultDisplayGrid.IsBusy = true;
tSearch = GetStudentResults(ctsSearch.Token);
await tSearch;
ResultDisplayGrid.IsBusy = false;
}
在上面的代码中,我们将取消上一个任务,然后尝试再次运行GetStudentResults。在您的GetStudentResults方法中,您将需要找到可以插入的位置:
if(token.IsCancellationRequested)
return Task.FromResult(false); //Replace this return type with whatever suits your GetStudentResults return type.
我的GetStudentResults方法是:
private Task<bool> GetStudentResults(CancellationToken token)
{
for(int i = 0; i < 10000; i++)
{
if (token.IsCancellationRequested)
return Task.FromResult(false);
Console.WriteLine(i);
}
return Task.FromResult(true);
}
有人可能还有其他想法,但对我来说,这是最简单的方法。
答案 2 :(得分:0)
您将需要使用CancellationTokenSource获得令牌,您可以跟踪该令牌是否通过重新输入取消了任务。
private CancellationTokenSource tokenSource;
public async void Search(string name)
{
this.tokenSource?.Cancel();
this.tokenSource = new CancellationTokenSource();
var token = this.tokenSource.Token;
this.IsBusy = true;
try
{
// await for the result from your async method (non void)
var result = await this.GetStudentResults(name, token);
// If it was cancelled by re-entry, just return
if (token.IsCancellationRequested)
{
return;
}
// If not cancelled then stop busy state
this.IsBusy = false;
Console.WriteLine($"{name} {result}");
}
catch (TaskCanceledException ex)
{
// Canceling the task will throw TaskCanceledException so handle it
Trace.WriteLine(ex.Message);
}
}
如果令牌将IsCancellationRequested设置为true,那么您的GetStudentResults也应考虑令牌并停止后台处理。