Roy Osherove

View Original

Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

Wouldn't it be cool if you could write something like this in your winform apps? :

[RunInUIThread]
protected virtual void DoSomeUIStuff()
{
this.Text = "hey"
}

Download the binary files from here. (here are the source files)

-----------------

Update:  Hammet notes that such a facility exists in Castle, also here's a thread about it. I feel that it is too verbose and not lightwhegith enough for me, and not as easily extensible as my version, but it's good to know that such a thing already exists!

---------------------------

Inspired by Ayende's talk at devTeach where he showed using Castle Windsor, I decided to take a closer look at how to implement a UI-Thread-Safe solution for winforms, based on his talk.

With a little help from Oren, it took about 5 minutes to implement the basics using the wonderful framework known as DynamicProxy2, part of the Castle project.

During the past couple of days I've refactored this into a more general framework for adding custom attributes which intercept method calls on any object, and allow you to easily create your own attributes with such interception abilities.

The only catches are that for this to work

  • the methods you intercept should be marked as virtual.
  • the objects which contain these methods should not be created directly, but through a special factory class which generates a proxy that does the hard interception work.

Here's how simple it is to implement a UI-Thread safe method in a Winform application:

  1. Download the binary files from here. (here are the source files)
  2. unzip the 3 dll files (osherove.simpleinterception, Castle.Core and Castle.DynamicProxy2) into a folder
  3. Create a new winform project and add a reference only to osherove.SimpleInterception.dll.
  4. Add a method named "virtual void whatever()" to Form1 class that looks like this with the new RunInUIThread attribute from the dll you just referenced:

[RunInUIThread]
protected virtual void Whatever()
{
this.Text = "hey"
}

  1. Add a button to the form and in its event handler start a new thread which calls the whatever() method like this:

protected virtual void button1_Click(object sender, EventArgs e)
{
Thread test = new Thread(new ThreadStart(Whatever));
test.Start();
}

 

  1. Change the code in the Main() method under program.cs to create the form using the special factory:

Form f = AOPFactory.Create<Form1>();
Application.Run(f);

 

That's it! The Whatever() method will always run in the UI thread, no matter what thread calls it.

 

Here's how easy it is to add your own intercepting Attribute:

Simply derive from AOPAttributeBase:

 

[AttributeUsage(AttributeTargets.Method)]
public class CustomLoggingAttribute:AOPAttributeBase
{
   protected override void InvokeBefore(AOPInvocation invocation)
   {
     Console.WriteLine("before method {0}...", invocation.Method.Name);
   }

   protected override void InvokeAfter(AOPInvocation invocation)
   {
    Console.WriteLine("after method {0}...", invocation.Method.Name);
   }
}

Download the binary files from here. (here are the source files)