CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Grant Killian's Blog

No, this has nothing to do with beer -- but maybe it should?

More on Asynchronous Stuff

I’ve been posting a bit on delegates and asynchronisity (wasn’t that a song by the Police?), and I just finished a messaging implementation for a project that speaks to this topic.  I've simplified it some, hoping it's easier to follow this way.  I’m using C# in this example, but many of the concepts apply to the VB.Net folks too.

 

The Problem: a long-running operation (minutes or hours in duration) needs to process without preventing the user from doing other things with the application; the user needs to have updates as to the progress of the operation.

 

The Solution Level 1: I decided to implement a winform application (to avoid timeouts and other ugly async issues with a web solution), using a progress bar to show how the long-running operation was doing, and display a dialog box when the long running task was completely finished.  The long-running operation would be invoked asynchronously.

 

The Solution Level 2:  The trick is communicating between the operation and the windows application.  I need a thread-safe means to invoke methods on the windows application from within the asynchronous, long-running operation.

 

The Solution Level 3:  I need to launch the long-running operation asynchronously (delegate #1), and I need to communicate with the windows app from within the long-running operation (delegate #2). 

I started by building my own EventArgs class:

            public class StatusEventArgs : EventArgs

            {

                        public bool isComplete;

                        public int TotalProgress;

                        public int ProgressSoFar;

                        public StatusEventArgs( int totalProgress, int progressSoFar )

                        {

                                    this.isComplete = false;

                                    this.TotalProgress = totalProgress;

                                    this.ProgressSoFar = progressSoFar;

                        }

            }

This class extends the behaviour of the .Net EventArgs class and you can pass this object around in lieu of the generic EventArgs class; it contains the specific information I’m interested in communicating between my asynchronous operation and the windows client.  File this away as this is a handy tactic to bring into your general .Net programming.

 

Next, I need a delegate (think typesafe function pointer) to invoke from my asynchronous operation:

public delegate void showProgressDelegate( object sender, StatusEventArgs e );

 

My long-running operation will accept a showProgressDelegate as a parameter to the method and make calls to the windows app via the delegate whenever it needs.  Here’s a condensed form of the long-running operation:

                        public void populateProducts( showProgressDelegate ShowProgress )

                        {

StatusEventArgs e = new StatusEventArgs(  0, 10 );                                

ShowProgress( sender, e ); //call to win app                      

                                    for( int x = 0; x < 10; x++ )

                                    {

                                                System.Threading.Sleep( 5000 );

                                                e.ProgressSoFar+=1;

                                                ShowProgress( sender, e ); //call to win app

                                    }

                                    e.isComplete = true;

                                    ShowProgress(sender, e); //final call to win ap

                        }

You can see how convenient it is to have your own custom EventArgs class; I’m incrementing the progress while we loop through our time consuming process.  Every call to ShowProgress communicates back to the windows client courtesy of .Net delegates. 

 

Let me go ahead and show the windows client code (note that eManagerLib is the name of my class library containing the long-running operation and related code):

public delegate void ShowProgressHandler(object sender, eManagerLib.StatusEventArgs e);

private void btn_Click(object sender, System.EventArgs e)

{

eManagerLib.showProgressDelegate del =

new eManagerLib.showProgressDelegate( ShowProgress );

            eManagerLib.ingramUtil obj = new eManagerLib.ingramUtil();

            ClientShowProgressDelegate  cliDel =

new ClientShowProgressDelegate( obj.populateProducts );

            cliDel.BeginInvoke( del, null, null );

            btn.Enabled = false;

}

void ShowProgress( object sender, eManagerLib.StatusEventArgs e)

{

if( this.InvokeRequired == false )

            {

                        ShowProgressHandler showProgress =

new ShowProgressHandler( ShowProgress );

                        Invoke( showProgress, new object[] { sender, e } );

            }

            else

            {

                        progressBar1.Maximum = e.TotalProgress;

                        progressBar1.Value = e.ProgressSoFar;

                        if( e.isComplete )

                        {

                                    MessageBox.Show( "Finished" );

                        }

            }

}

In a nutshell, in our button click event we construct a new showProgressDelegate (del) and a new ClientShowProgressDelegate (cliDel).  Our cliDel delegate points to the long-running operation (obj.populateProducts) and we invoke this delegate using BeginInvoke and pass the second delegate (del) for use in the populateProducts method.  When populateProducts chooses to notify the windows application, it will call the windows app’s ShowProgress method.  The two delegates provide our asynchronous messaging system: ClientShowProgressDelegate launches our asynchronous operation, the ShowProgressDelegate calls back to the windows app to update the client.  Our ShowProgress method receives the calls from the long-running operation, synchronizes them onto the proper UI thread (thanks again Peter), and updates the windows form appropriately.

 

Note that I’ve omitted error handling and some other tidbits in the hopes of making this example clear and terse.  You can capitalize on this sort of framework to solve all sorts of messaging problems  -- no need to continuously poll a database, file, or static variable, let the components of your system send their own messages with a little .Net delegate elbow grease.

 

Happy .Netting!



Comments

Reggie Van Wyk said:

Have read this article with much interest. I am trying to do something similar in VB.Net but I'm trying to update a PictureBox.Image instead of a Progressbar. I think I can translate the code to VB.Net except for:

eManagerLib.ingramUtil obj = new eManagerLib.ingramUtil(); -- what is this exactly?

ClientShowProgressDelegate cliDel = new ClientShowProgressDelegate( obj.populateProducts ); -- no mention of this Delegate in the article either.

Any help would be much appreciated!!
# October 1, 2004 8:56 PM

Grant said:

Those lines are paticular to the application I used as an example. The line with the ingramUtil object is just instantiating a new instance of ingramUtil -- nothing fancy there. As for ClientShowProgressDelegate, I write "ClientShowProgressDelegate launches our asynchronous operation" it is the 2nd delegate in my example. It would be delcared like our 1st delegate.
# October 4, 2004 1:47 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!