首页 > 解决方案 > 在重入调用上使用异步方法时如何处理 WPF 中的忙指示符?

问题描述

在 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;
}

在此处输入图像描述 在此处输入图像描述

标签: c#wpfasync-await

解决方案


自最新评论以来考虑到这一点,它将需要一个更复杂的解决方案,涉及适当的任务管理,这在帮助他人时开始超出我的舒适区。

在我看来,最快和最简单的方法是在搜索开始后阻止用户与文本框或 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);
}

有人可能有其他想法,但对我来说,这些是最简单的方法。


推荐阅读