Search The Blog
Latest Posts
from 5whys.com
Twitter: @RoyOsherove
About this site

TDD in .NET Online Course

TDD and BDD in Ruby Online Course

 

Subscribe!

This site aims to connect all the dots of my online activities - from tools, books blogs and twitter accounts, to upcoming conferences, engagements and user group talks.

« It's Time For Violence: A song about Databases | Main | Learning new technologies: Is C# a XAML for the CLR? »
Saturday
May192007

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)

PrintView Printer Friendly Version

Reader Comments (2)

I got your example to work on a simple form project. Neat!

But what if you have something like this, a model-view-controller solution?

Project 1- Main UI
Project 2- User Controls
Project 3- Business classes

So the code below goes into Project 1

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

However, in my application user controls in Project 2 create worker threads that run methods in Project 3 (actually using your own BackgroundWorkerEx which is awesome btw).

So I added [RunInUIThread] to the methods in Project 2 where cross-thread errors have occurred in the past. Using BeginInvoke works for this - no cross-thread errors occur. But I want to use your method to make the code simpler. But it doesn't work yet. The same cross-threading errors occur as if the BeginInvoke code isn't there.

Any advice on what to do? Maybe it is something simple but I don't see it. Thanks!

January 15, 2011 | Unregistered Commenterzagnet

I created a small test solution that includes the User Control project's user control which is added to the Main UI form which gets the cross-thread error. May I send it to you?

January 15, 2011 | Unregistered Commenterzagnet

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Web Analytics