Browse DevX
Sign up for e-mail newsletters from DevX


Asynchronous Windows Forms Programming : Page 5

Learn how to implement BackgroundWorker in Visual Studio .NET 2003 so you can benefit from its elegant programming model and transition transparently into Windows Forms 2.0 in the future.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Implementing BackgroundWorker with .NET 1.1 (cont.)
OnRunWorkerCompleted() passes to ProcessDelegate() the delegate to invoke and the invocation parameters. Since Delegate is the base class of all delegates, ProcessDelegate() can accept any delegate. The use of the params object array means the caller to ProcessDelegate() can pass it any combination of parameters and the compiler will package it into an object array when calling ProcessDelegate(). ProcessDelegate() first needs to check that the delegate passed in has targets in it, because if it does not, it will be set to null and will be inaccessible. There is a little-known race condition when dealing with delegates in a multithreaded environment. It is not good enough to just check the delegate because between the time you check and the time you access the delegate, there could have been a thread context switch and somebody else could have removed the targets, causing your access to throw an exception. Fortunately, there is a simple workaround—delegates are immutable, so you can safely copy the delegate to a temporary variable, check on it, and then access the temporary variable, as done by ProcessDelegate():

void ProcessDelegate(Delegate del,params object[] args) { Delegate temp = del; if(temp == null) { return; } Delegate[] delegates = temp.GetInvocationList(); //Rest of the method }

You could potentially have multiple parties interested in the event represented by the delegate, and any or all of these parties could be Windows Forms objects that require the special use of ISynchronizeInvoke. ProcessDelegate() must therefore process each of them individually. It retrieves the array of targets in the delegate by calling GetInvocationList(), which returns an array of individual delegates.

For each delegate in that collection, ProcessDelegate() then calls the private helper method InvokeDelegate(), passing the delegate as a parameter along with the arguments:

foreach(Delegate handler in delegates) { InvokeDelegate(handler,args); }

InvokeDelegate() is defined as :

void InvokeDelegate(Delegate del,object[] args);

InvokeDelegate() abstracts the act of invoking a delegate, whether you use it for targeting a method on a Windows Forms object or not.

The first thing InvokeDelegate() needs to do is to obtain from the delegate the object it targets. It does this using the Target property of the delegate. Once InvokeDelegate() has the target object, it queries it for ISynchronizeInvoke:

ISynchronizeInvoke synchronizer = del.Target as ISynchronizeInvoke;

If the target object does not support ISynchronizeInvoke then synchronizer will be set to null, in which case InvokeDelegate() simply invokes the delegate using the DynamicInvoke() method that every delegate supports, passing in the arguments for the target method:


You must use DynamicInvoke() because InvokeDelegate() does not know the exact type of delegate it is invoking. If synchronizer is not null, InvokeDelegate() checks whether invoke is required, and if so, it uses the Invoke method to marshal the call to the correct thread:

if(synchronizer != null)//A Windows Forms object { if(synchronizer.InvokeRequired == false) { del.DynamicInvoke(args); return; } try { synchronizer.Invoke(del,args); } catch {} }

The implementation of ReportProgress() is straightforward: It constructs a new object of type ProgressChangedEventArgs wrapping the percent argument, then it calls the protected virtual OnProgressChanged() method, which calls ProcessDelegate() on the ProgressChanged delegate.

The rest of the methods and properties of BackgroundWorker are merely thread-safe accessors to the corresponding member variables.

Figure 2: The BackgroundWorker component in VS.NET 1.1.
Finishing Touches
To add my BackgroundWorker component to the Components toolbox, right-click on the toolbox and select Add/Remove Items... from the pop-up context menu. Browse to the location of the WinFormsEx assembly and add it to the toolbox. I wanted my BackgroundWorker to have the same great icon as in .NET 2.0, so after I copied the icon from .NET 2.0 (all rights to the icon go to Microsoft), I added it to the WinFormsEx assembly as an embedded resource. To assign the icon to the BackgroundWorker class I used the ToolboxBitmap attribute. The attribute accepts a type identifying the assembly where the resource is embedded and the name of the resource. Once you drop BackgroundWorker on a form, you will see it under the form, just like in .NET 2.0. You can use the Visual Designer to set the BackgroundWorker properties and events, as shown in Figure 2. Your code that uses BackgroundWorker today will transparently be able to use BackgroundWorker in .NET 2.0, simply by removing the reference to the WinFormsEx namespace, because the two components are polymorphic and function in an identical manner.

Juval Löwy is a software architect and the principal of IDesign, a consulting and training company focused on .NET design and .NET migration. His latest book is "Programming .NET Components" (O'Reilly, 2003). Juval is Microsoft's Regional Director for the Silicon Valley, helping the industry adopt .NET. Juval is a frequent speaker at major international software development conferences. Contact him at www.idesign.net, where you can download his C# Coding Standard.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date