UI Threading Helper Classes
One of the most cumbersome things you will ever have to do with Winforms is updating your User interface while receiving events from another thread. For example, you have a long-running method, but you want to show a progress bar while it runs. To make the application responsive during execution of this method, you decide to run the method in a new Thread. No problems! However, you can't just call methods on the progress bar control from the running method. The reason lies in the fact that the Progress bar is part of the Form thread, which is different from the Method thread. Calling methods on another thread can lead to application hangs and other great stuff.
The only way to call methods on the progress bar control is by calling the Form.Invoke() method, which takes a delegate to a method which would perform this operation. Form.Invoke() performs the invocations using the Form's thread, which is the "good" way to perform these operations.
This means that every time you want to update your UI from another thread, you'd need to create 2 separate methods and 2 separate delegates. One method and delegate to recieve the event from the thread which then "Invoke"s another delegate to a method on the form, whcih actually updates the UI. Agh. Isn't that wonderful?
Enter the UI Threading Helper Classes!
Using these babies, It’s much easier to create a delegate and call it from another thread. They are comprised of two classes: UIThreadManager, and UIThreadHandler.
UIThreadHandler is the one doing all the hard work. It works simply by holding a reference to the delegate on the form which you would like to Invoke, and a reference to the Actual form instance. What it saves you is from writing those most common Delegate stubs which would call Invoke on the form.
To do this , it contains a public method called BaseEventHandler which is overloaded by 6 variations . The most common variations on which Event Handler methods are written. So, it will contain the signature for the EventHandler Delegate type, and also an Empty signature, single object, 2 objects, int and string. You can also extend this functionality by either adding overloads or deriving from this class.
UIThreadManager allows for easy creation of new UIThreadHandler instances, and holds them in memory using an ArrayList. Very simple.
Here are three variations on using these classes:
1) I have a long running loop in another thread which throws an EventHandler type of event. I want to receive this event and call a UI updating method:
//m_uimgr is an instance of UIThreadManager
EventThrower e = new EventThrower();
e.OnEventThrow+=m_uimgr.NewHandler(this,new EventHandler(this.UpdateProgressBar));
e.Start();
As you can understand, m_uimgr creates an instance of a new UIThreadHandler, loads it with the provided form and delegate, and returns a delegate to one of its stub methods which, when called, calls Invoke() on the main Form.
2) I have an event other than EventHandler type, Or I have derieved from the UIThreadHandler and would like to use a different stub method of the new class as a delegate to my thread events.
EventThrower e = new EventThrower();
UIThreadHandler h = m_uimgr.CreateHandler(this,new EventHandler(this.UpdateProgressBar));
e.OnEventThrow+=new MyHandlerDelegate(h.BaseEventHandler);
e.Start();
This technique allows you to either add more overloads to the BaseEventHandler Method, for more flexibility.
3) I have derived from UIThreadHandler and created by own Delegate Handlers
EventThrower e = new EventThrower();
UIThreadHandler h = new UIThreadHandler(this,new MyEventHandler(this.UpdateProgressBar));
m_uimgr.AddHandler(h);
e.OnEventThrow+=new MyHandlerDelegate(h.CustomEventHandler);
e.Start();
Here’s the code for these classes. Have fun. And , if you have some more ideas and suggestions – I’d like to know!
---------Copy And Paste the following code into an empty .Cs File--------
using System;
using System.Collections;
using System.Windows.Forms;
namespace Royo.UIThreading
{
///
/// Manages a collection of UIThreadHandlers
/// And allows easy creation of UIThreadHandler Instances
///
public class UIThreadManager
{
//Priate collection of Thread Handlers
protected ArrayList m_ThreadHandlers = new ArrayList();
///
/// All instances of UIThreadHandlers currently in memory
///
public ArrayList ThreadHandlers
{
get
{
return m_ThreadHandlers;
}
}
public UIThreadManager()
{
}
///
/// Creates and instance of a UIThreadHandler class
/// and adds it to the collection of current Handlers
///
/// The target Control on which the new handler will invoke a delegate
/// The target delegate whcih will be invoked on the Control instance
///
public UIThreadHandler CreateHandler(Control TargetControl,EventHandler HandlerDelegateInControl)
{
UIThreadHandler handler = new UIThreadHandler(TargetControl,HandlerDelegateInControl);
m_ThreadHandlers.Add(handler);
return handler;
}
///
/// Creates and instance of a UIThreadHandler class
/// and adds it to the collection of current Handlers
///
/// The target Control on which the new handler will invoke a delegate
/// The target delegate whcih will be invoked on the Control instance
///
public UIThreadHandler CreateHandler(Control TargetControl,Delegate HandlerDelegateInControl)
{
UIThreadHandler handler = new UIThreadHandler(TargetControl,HandlerDelegateInControl);
m_ThreadHandlers.Add(handler);
return handler;
}
///
/// Creates an instance of a new UIThreadHandler Class
/// And adds it to the collection of handlers.
/// Then returns a new delegate of type EventHandler
/// which points to a method on the new UIThreadHandler.
///
/// The target Control on which the new handler will invoke a delegate
/// The target delegate whcih will be invoked on the Control instance
///
public EventHandler NewHandler(Control TargetControl,EventHandler HandlerDelegateInControl)
{
UIThreadHandler handler = CreateHandler(TargetControl,HandlerDelegateInControl);
return new EventHandler(handler.BaseEventHandler);
}
///
/// Removes a UIThreadHandler from the Collection of UIHandlers
///
/// UIThreadHandler Instance to remove
public void RemoveHandler(UIThreadHandler handler)
{
try
{
m_ThreadHandlers.Remove(handler);
}
catch(Exception e){}
}
///
/// Add a custom handler to the current collection
///
///
public void AddHandler(UIThreadHandler handler)
{
try
{
m_ThreadHandlers.Add(handler);
}
catch(Exception e){}
}
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////UIThreadHandler/////////////////
//////////////////////////////////////////////////////////////////
///
/// This class represents an instance of a UI method caller
/// Its only job is to recieve a delegate found on
/// an instance of a control(usually a form) and to invoke that delegate
/// When it recieved an event
///
public class UIThreadHandler
{
//The delegate which is called on the UI thread using "m_TargetControl.Invoke()"
protected System.Delegate m_ControlHandler;
//The Control (Usualoy A Form) on which to Invoke a UI delegate
protected Control m_TargetControl;
///
/// Creates an instance of UIThreadHandler
/// Along with its associated Form and Delegate
/// on which to invoke UI thread-related events
///
/// The Form instance on which UI actions are performed
/// A delegate pointing to a method on the Form instance
/// Which updates a control on the Form
///
/// EventThrower e = new EventThrower();
/// e.OnEventThrow+=new EventHandler(m_uimgr.CreateHandler(this,new ///EventHandler(Handler11)).BaseEventHandler);
/// e.Start();
///
public UIThreadHandler(Control TargetControl,Delegate HandlerDelegateInControl)
{
m_TargetControl= TargetControl;
m_ControlHandler = HandlerDelegateInControl;
}
///
/// A method which matches most basic event handlers
/// Use this to create a delegate which recieves events
/// which need to trigger UI actions in the form thread.
///
///
///
public void BaseEventHandler(object source, EventArgs args)
{
try
{
if(m_ControlHandler!=null)
{
object[] arr = new object[]{source,args};
m_TargetControl.Invoke(m_ControlHandler,arr);
}
}
catch(Exception e)
{
string se =e.ToString();
System.Diagnostics.Debug.WriteLine(e.ToString());
}
}
public void BaseEventHandler(object source, object args)
{
if(m_ControlHandler!=null)
{
object[] arr = new object[]{source,args};
m_TargetControl.Invoke(m_ControlHandler,arr);
}
}
public void BaseEventHandler()
{
if(m_ControlHandler!=null)
{
m_TargetControl.Invoke(m_ControlHandler);
}
}
public void BaseEventHandler(int arg)
{
if(m_ControlHandler!=null)
{
m_TargetControl.Invoke(m_ControlHandler,new object[]{arg});
}
}
public void BaseEventHandler(long arg)
{
if(m_ControlHandler!=null)
{
m_TargetControl.Invoke(m_ControlHandler,new object[]{arg});
}
}
public void BaseEventHandler(object arg)
{
if(m_ControlHandler!=null)
{
m_TargetControl.Invoke(m_ControlHandler,new object[]{arg});
}
}
public void BaseEventHandler(string arg)
{
if(m_ControlHandler!=null)
{
m_TargetControl.Invoke(m_ControlHandler,new object[]{arg});
}
}
///
/// Explicitly Invoke the Delegate on the Form Instance
/// using the specifies parameters
///
/// any parameters whcih need to be sent to the
/// delegate on the Form Instance
public void Invoke(params object[] args)
{
m_TargetControl.Invoke(m_ControlHandler,args);
}
}
}