Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C#

Running an Async Event Handler on the UI thread (with lambdas and extension methods!)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
7 Jun 2012CPOL2 min read 13.2K   5  
Run an Async Event Handler on the UI thread.

So, it’s been a freakishly long time since my last post here. 

I’ve been trying to get better… I even wrote out a list of topics I wanted to write about.  So, let’s start talking about them.

As we do more and more work in our applications asynchronously, we’re confronted with the problem of how to communicate the status of the background thread to our users. Firing an event seems like the best way, except anything the user can see has to be done by the UI thread, which is specifically not the thread the background task is running on.

WinForms offers a means of redirecting execution onto the UI thread, the Form.Invoke method, but using it makes life difficult. This is particular true when you throw lambdas into the mix. Lambdas made it quite easy to write a simple event handler:

C#
backgroundAction.StepCompleted +=
         (s, ea) => {statusMsg.Text = String.Format("Step #{0} Completed", ea.StepNum);}

But when we add is thread marshaling, it gets messy:

C#
backgroundAction.StepCompleted +=
 (s, ea) =>{this.Invoke((s1,ea1)=>{statusMsg.Text = String.Format("Step #{0} Completed", ea.StepNum);}, s,ea);

And that’s with always marshaling it. If the event could be fired from both a background thread or from the main thread depending on the context, then you should check the InvokeRequired property, if call the action directly if it’s not needed. But complicates our lives, making us split the action out into a named function:

C#
backgroundTask.StepCompleted += ((s, ea) =>
{
  if (this.InvokeRequired)
    this.Invoke(UpdateStatus, s, ea);
  else
    UpdateStatus(s,ea);
});
// ...
void UpdateStatus(object sender, MessageEventArgs ea)
{
  statusMsg.Text = String.Format("Step #{0} Completed", ea.StepNo);
}

Yech! We have a lambda, but it we’re using it for the boiler-plate code and that we will probably have to repeat. And the real method we want to perform is in a separate function.

What we need is a handle utility function which will take a method reference, or, better yet, a lambda, and package it up as we need it. Then we could write it like:

C#
backgroundTask.StepCompleted += 
   ToUIThread<StepEventArgs>((s, ea) => statusMsg.Text = String.Format("Step #{0} Completed", ea.StepNo));

The tricky part about this is that it must take a function as a parameter, and return a function. Plus, the compiler can’t figure out the type of the second parameter by itself, so we have to give it some help.  And, we we’ll need a non-generic version, for event which are defined as EventHandle instead of EventHandler<TEventArgs>. Make it an extension method on Form, and we’ve got:

C#
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

public static class FormExt
{
    static public EventHandler<TEventArgs> ToUIThread<TEventArgs>(this Form frm, 
           EventHandler<TEventArgs> handler) where TEventArgs : System.EventArgs
    {
        return ((sender, e) =>
               {
            if (frm.InvokeRequired)
                frm.Invoke(handler, sender, e);
            else
                handler(sender, e);
               });
    }

    static public EventHandler ToUIThread(this Form frm, EventHandler handler)
    {
        return ((sender, e) =>
            {
            if (frm.InvokeRequired)
                frm.Invoke(handler, sender, e);
            else
                handler(sender, e);
               });
    }
}
This article was originally posted at http://feeds.feedburner.com/HonestIllusion

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) NovelTheory LLC
United States United States
20+ years as a developer : Assembly, C, C++ and C# (in that order) with sidelines in ASP/VBScript, ASP.Net, JavaScript, Perl, QuickBasic, VisualBasic, plus a few others which I'm not going to mention because if I did someone might ask me to use them again (shudder)

Microsoft MVP in VC++ (1994-2004)

I also run www.NJTheater.com as a hobby.

Full resume & stuff at NovelTheory.com

Underused blog at HonestIllusion.com

Comments and Discussions

 
-- There are no messages in this forum --