Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Raise Events in .NET on the main UI thread

I'm developing a class library in .NET that other developers will consume eventually. This library makes use of a few worker threads, and those threads fire status events that will cause some UI controls to be updated in the WinForms / WPF application.

Normally, for every update, you would need to check the .InvokeRequired property on WinForms or equivalent WPF property and invoke this on the main UI thread for updating. This can get old quickly, and something doesn't feel right about making the end developer do this, so...

Is there any way that my library can fire/invoke the events/delegates from the main UI thread?

In particular...

  1. Should I automatically "detect" the "main" thread to use?
  2. If not, should I require the end developer to call some (pseudo) UseThisThreadForEvents() method when the application starts so I can grab the target thread from that call?
like image 594
Brandon Avatar asked Nov 09 '09 03:11

Brandon


People also ask

What is raising an event in C#?

C# - Events. An event is a notification sent by an object to signal the occurrence of an action. Events in . NET follow the observer design pattern. The class who raises events is called Publisher, and the class who receives the notification is called Subscriber.

What is UI thread in C#?

A UI thread creates UI elements and waits and responds to events like mouse clicks and key presses. You can only access the UI elements from the UI thread. There are two types of threads: background and foreground. A UI thread is an example of a foreground thread.

What is UI thread in WPF?

The UI thread queues work items inside an object called a Dispatcher. The Dispatcher selects work items on a priority basis and runs each one to completion. Every UI thread must have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread.


1 Answers

Your library could check the Target of each delegate in the event's invocation list, and marshal the call to the target thread if that target is ISynchronizeInvoke:

private void RaiseEventOnUIThread(Delegate theEvent, object[] args) {   foreach (Delegate d in theEvent.GetInvocationList())   {     ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;     if (syncer == null)     {       d.DynamicInvoke(args);     }     else     {       syncer.BeginInvoke(d, args);  // cleanup omitted     }   } } 

Another approach, which makes the threading contract more explicit, is to require clients of your library to pass in an ISynchronizeInvoke or SynchronizationContext for the thread on which they want you to raise events. This gives users of your library a bit more visibility and control than the "secretly check the delegate target" approach.

In regard to your second question, I would place the thread marshalling stuff within your OnXxx or whatever API the user code calls that could result in an event being raised.

like image 55
itowlson Avatar answered Sep 17 '22 15:09

itowlson