在 2 个等待句子之间更新 UI

本文关键字:之间 更新 UI 句子 等待 | 更新日期: 2023-09-27 18:35:21

我正在尝试在一个(相当简单的)应用程序中实现async-await的东西。我的目标是在等待之间更新一个繁忙的指示器。

我不知道是什么,但我认为我在理解异步等待的东西方面缺少一些必不可少的东西。

private async void StartTest(object obj)
{
    try
    {
        this.IsBusy = true;
        this.BusyMessage = "Init..."
        await Task.Delay(7000);

        var getData1Task = this.blHandler.GetData1Async();
        this.BusyMessage = "Retreiving data...";
        this.result1 = await getDeviceInfoTask;
        this.result2 = await this.blHandler.GetData2Async();
        this.BusyMessage = "Searching...";
        this.result3 = await this.blHandler.GetData3();
    }
    finally
    {
        this.IsBusy = false;
        this.BusyMessage = string.empty;
    }
}

busyIndicator具有与IsBusyBusyMessage的绑定。执行此代码时,我确实会得到显示"Init..."的 busyIndicator,但它永远不会更改为"检索数据..."或"正在搜索..."。更糟糕的是:UI在执行最终GetData3时完全冻结。

在 2 个等待句子之间更新 UI

最有可能

GetData1AsyncGetData2AsyncGetData3是同步方法(也就是说,我猜虽然它们确实返回了一个Task,但它们会同步完成所有工作)。 在这种情况下,await不会挂起该方法(因为返回的Task将是已完成的任务)。 因此,该方法将作为一个大型同步方法一直持续下去,并且 UI 将永远没有机会更新(因为它在此期间不会泵送任何消息)。

如果您想要的不仅仅是猜测,请向我们展示这三种方法的代码。

听起来您实际上想在后台线程上执行同步方法,然后在 UI 代码中异步等待它完成。

这正是Task.Run()所做的。
它需要一个委托在 ThreadPool 中运行,然后返回一个await任务,该任务为您提供结果。

您能否尝试通过调度程序推送消息。

    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
                        { this.BusyMessage = "Retreiving data..."; }));
    // Do Something 

   App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
                    { this.BusyMessage = "Filtering  data..."; }));
   // Do Something

这是一个工作副本,其中包含我的一些假设。 希望对您有所帮助。 我在正在运行的 WPF 应用程序中对其进行了测试。 请注意,您发布的内容中没有任何保护措施,以确保这不是"双重运行"(无论 IsBusy的值如何,我们都可以开始,StartTest 应该确保这一点)。

public class StackWork : ViewModelBase
{
    private class MyHandler
    {
        private async Task<string> GetDataAsync(string result)
        {
            return await Task<string>.Run(() =>
                {
                    Thread.Sleep(5000);
                    return result;
                });
        }
        public async Task<string> GetData1Async()
        {
            return await GetDataAsync("Data1");
        }
        public async Task<string> GetData2Async()
        {
            return await GetDataAsync("Data2");
        }
        public async Task<string> GetData3()
        {
            return await GetDataAsync("Data3");
        }
    }
    private bool IsBusy { get; set; }
    private string _message = "";
    public string BusyMessage
    {
        get { return _message; }
        set { _message = value; RaisePropertyChanged("BusyMessage"); }
    }
    private MyHandler blHandler = new MyHandler();
    private Task<string> getDeviceInfoTask;
    private string result1 { get; set; }
    private string result2 { get; set; }
    private string result3 { get; set; }
    public StackWork()
    {
        getDeviceInfoTask = Task<string>.Run(() =>
            {
                return ("device info");
            });
    }
    public async void StartTest(object obj)
    {
        try
        {
            this.IsBusy = true;
            this.BusyMessage = "Init...";
            await Task.Delay(7000);
            var getData1Task = /*this*/await/*was missing*/ this.blHandler.GetData1Async();
            this.BusyMessage = "Retreiving data...";
            //assuming this was Task.Run, put that in constructor
            this.result1 = getDeviceInfoTask/*this*/.Result/*was missing*/;
            this.result2 = await this.blHandler.GetData2Async();
            this.BusyMessage = "Searching...";
            //This was a little confusing because the name doesn't imply it is 
            //async, but it is awaited
            this.result3 = await this.blHandler.GetData3();
        }
        finally
        {
            this.IsBusy = false;
            this.BusyMessage = string.Empty;
        }
    }
}