简体   繁体   中英

How do I update the GUI on the parent form when I retrieve the value from a Task?

I think I'm missing something obvious here, but how do I update the GUI when using a task and retrieving the value? (I'm trying to use await/async instead of BackgroundWorker)

On my control the user has clicked a button that will do something that takes time. I want to alert the parent form so it can show some progress:

private void ButtonClicked()
        {
            var task = Task<bool>.Factory.StartNew(() =>
            {
                WorkStarted(this, new EventArgs());
                Thread.Sleep(5000);
                WorkComplete(this, null);
                return true;
            });

            if (task.Result) MessageBox.Show("Success!");//this line causes app to block
        }

In my parent form I'm listening to WorkStarted and WorkComplete to update the status bar:

myControl.WorkStarting += (o, args) =>
                {
                    Invoke((MethodInvoker) delegate
                        {
                            toolStripProgressBar1.Visible = true;
                            toolStripStatusLabel1.Text = "Busy";
                        });

                };

Correct me if I'm wrong, but the app is hanging because "Invoke" is waiting for the GUI thread to become available which it won't until my "ButtonClicked()" call is complete. So we have a deadlock. What's the correct way to approach this?

You're blocking the UI thread Task.Result blocks until the task is completed.

Try this.

private async void ButtonClicked()
{
     var task = Task<bool>.Factory.StartNew(() =>
     {
            WorkStarted(this, new EventArgs());
            Thread.Sleep(5000);
            WorkComplete(this, null);
            return true;
     });

     await task;//Wait Asynchronously
     if (task.Result) MessageBox.Show("Success!");//this line causes app to block
}

You can use Task.Run to execute code on a background thread. The Task-based Asynchronous Pattern specifies a pattern for progress updates, which looks like this:

private async void ButtonClicked()
{
  var progress = new Progress<int>(update =>
  {
    // Apply "update" to the UI
  });
  var result = await Task.Run(() => DoWork(progress));
  if (result) MessageBox.Show("Success!");
}

private static bool DoWork(IProgress<int> progress)
{
  for (int i = 0; i != 5; ++i)
  {
    if (progress != null)
      progress.Report(i);
    Thread.Sleep(1000);
  }
  return true;
}

If you are targeting .NET 4.0, then you can use Microsoft.Bcl.Async ; in that case, you would have to use TaskEx.Run instead of Task.Run . I explain on my blog why you shouldn't use Task.Factory.StartNew .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM