3
\$\begingroup\$

Let's say that I have a method that makes an HTTP request that will take a long time to finish. While that's happening the UI is not updating because I'm doing it in the UI's thread.

One solution is to:

  1. Show the "Loading" animation.
  2. Start the HTTP request on a thread.
  3. On the completion callback stop the "Loading" animation and work on the response.

That's all nice and good, but it means that I went from code that was sequential in nature to something that has callbacks or something similar (promises).

So, I'm trying to keep the sequential nature while updating the UI. Having something like await/async would help here, but even in that case the caller has to be aware of this (plus I need .NET 4.5, let's assume that this is not an option). So I came up with something that keep the sequential nature of the original call, doesn't affect the caller of the function makes the HTTP request but updates the UI.

So, here it is:

public class Async<T>
{
 public static T Run(Func<T> action)
 {
 var worker = new BackgroundWorker();
 var error = new Exception[] {null};
 var result = new[] {default(T)};
 // The original call will run in a separate thread.
 worker.DoWork += (sender, ea) => { result[0] = action(); };
 worker.RunWorkerCompleted += (sender, ea) => { error[0] = ea.Error; };
 worker.RunWorkerAsync();
 // While the work is being done in a separate thread, update the UI's event loop (is this safe???)
 while (worker.IsBusy)
 {
 Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
 Thread.Sleep(20);
 }
 // Re raise the exception if needed
 if (error[0] != null)
 throw error[0];
 return result[0];
 }
}
//Usage
var someVariable = 123;
var responseA = Async<T>.Run(DoSuperLongTask);
var responseB = Async<T>.Run(() =>
 {
 return DoSuperComplicatedTask(someVariable);
 });

It'll be something like

await Task.Run(() => DoWork());
await Task.Run(() => DoWork());

I'm not an expert in GUIs, and I'm especially not an expert in WPF. So my question to you is:

Is this safe to use?

So far it seems to work okay.

Malachi
29k11 gold badges86 silver badges188 bronze badges
asked Apr 3, 2014 at 15:25
\$\endgroup\$
1
  • \$\begingroup\$ "plus I need .NET 4.5" You don't, you can use async-await on .Net 4.0 using Microsoft.Bcl.Async. But you do need at least VS 2012. \$\endgroup\$ Commented Apr 3, 2014 at 18:56

1 Answer 1

2
\$\begingroup\$

Good you have a BackgroundWorker. If I understood correctly, let's say you want to update a button, whether to enable it or not. you can use this:

delegate void EnableButton(Button b, bool enable);
public void ActuallyEnableButton(Button b, bool enable)
{
 if (b.InvokeRequired)
 {
 EnableButton del = ActuallyEnableButton;
 b.Invoke(del, new object[] { b, enable });
 //in some cases add "return" statement
 }
 b.Enabled = enable;
}

Call ActuallyEnableButton(btn, true) for example, inside DoWork event of BackgroundWorker.

This applies to Label, TextBox, ComboBox, ListView, ProgressBar, etc. No need to use this code if you want to update ToolStripStatusLabel and ToolStripProgressBar.

Hope this helped.

answered Apr 8, 2014 at 4:50
\$\endgroup\$
2
  • \$\begingroup\$ Why NOT need to use this code if I want to update ToolStripStatusLabel and ToolStripProgressBar ? \$\endgroup\$ Commented Mar 13, 2017 at 16:31
  • \$\begingroup\$ Yup @Kiquenet. You can update those controls directly without a delegate function. \$\endgroup\$ Commented Mar 20, 2017 at 2:54

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.