views:

63

answers:

1

I'm working on an application that has a main form with a status bar at the bottom. The status bar contains a ProgressBar, and a Label that I want to use to show the user the current progress of some work that is being done. The status bar also contains a label which I want to use like a click-able Cancel button.

I've create asynchronous interfaces before like this, but they have always been based around a single action, and by using a background worker. But in this new program there are a number of different actions the user may envoke all of which I want to use this same status bar to show the progress of. So I'm trying to figure out a way to generalize and standardize the interface to these progress reporting controls in the status bar.

In some cases the asynchronous processes are created using a BackGroundWorker, yet in other cases I need to create an manage the secondary threads directly.

Here's some half finished skeleton code of what I've been thinking:

public partial class MyForm: System.Windows.Forms.Form
{
    ...SNIP...

    private void WorkProgressChanged(Object sender, EventArgs args)
    {
        ProgressChangedEventArgs backgroundWorkerArgs = args as ProgressChangedEventArgs;
        ProgressReport formReport = args as ProgressReport; //my own custom progress report class


       //tries to cast args to a Background worker args
        if (backgroundWorkerArgs != null)
        {
            // update UI based on backgroundWorkerArgs

        }
        else if (formReport != null)
        {
            // update UI basd on formReport

        }

        else 
        {
            //couldn't figure out what kind of progress report was sent
            //update UI based on args.ToString();

        }

    }


    private void cancelButtonToolStripLabel_Click(object sender, EventArgs e)
    {
        //calls cancel method of current processing
        if (this._currentWorkCancelAction != null)
        {
            _currentWorkCancelAction(); //envoke cancel requet
            cancelButtonToolStripLabel.Text = "Canceling"; //shows user that cancel request was made
            _currentWorkCancelAction = null; //disaccociates cancel button to prevent user from canceling twice
        }
    }


    private void WorkProcessCompleted(Object sender, EventArgs args)
    { 
        //Reset cancel button
        cancelButtonToolStripLabel.Text = "Cancel";
        cancelButtonToolStripLabel.Visible = false;

        //resets the status label and progress bar
        statusToolStripLabel.Text = "";
        toolStripProgressBar.Value = 0;


    }

....SNIP

}

So the status bar is updated by subscribing `WorkProgressChanged(Object sender, EventArgs args) to some event ', and ultimately reset when 'WorkProcessCompleted(Object sender, EventArgs args)' is envoked by a completion even. My cancel label (button) also needs to be associated and later dissociated with a delegate method that will request that whatever work is currently being done is canceled.

So each time work is done a bunch of stuff needs to happen. Event subscriptions are added/remove, delegate references are changed etc.. etc.. So I started wondering if there was some way I could encapsulate all these actions in to one or two reusable methods rather then writing duplicate code for each action that may take place.

The InitWorkProcess() method below shows how I am thinking this may work . Although I'm pretty sure this is not the right way to use the EventDescriptor class. I couldn't figure out any other way to reference an event as a method parameter. Maybe this isn't possible?

    public void InitWorkProcess(EventDescriptor workProgressChangedEvent, EventDescriptor workCompletedEvent, System.Action requestCancel)
    { 
        //subscribe to progress changed
        workProgressChangedEvent.AddEventHandler(this, this.WorkProgressChanged);
        this._workProgressChangedEvent = workProgressChangedEvent;

        //subscribe to process completed
        workCompletedEvent.AddEventHandler(this, this.WorkProcessCompleted);
        this._workCompletedEvent = workCompletedEvent;


        //enable cancel button
        if (requestCancel != null)
        {
            cancelButtonToolStripLabel.Visible = true;
            this._currentWorkCancelAction = requestCancel;
        }
    }

... and I'd change the WorkProgressComplete event handling method to unsubscribe the event relationships when the work is completed.

    private void WorkProcessCompleted(Object sender, EventArgs args)
    { 
        //Reset cancel button
        cancelButtonToolStripLabel.Text = "Cancel";
        cancelButtonToolStripLabel.Visible = false;

        //resets the status label and progress bar
        statusToolStripLabel.Text = "";
        toolStripProgressBar.Value = 0;


        //unsubscribes WorkProcessCompleted() and WorkProgressChanged() methods
        this._workCompletedEvent.RemoveEventHandler(this, this._workCompletedEvent);
        this._workCompletedEvent = null;

        this._workProgressChangedEvent.RemoveEventHandler(this, this._workProgressChangedEvent);
        this._workProgressChangedEvent = null;
    }

Does anyone have any suggestions on how I should set this up? Should I just forget about the InitWorkProcess() method and instead add/remove all the event/delegate relationships separately for each action? Or is there a better way entirely?

A: 

Why not just use ProgressChangedEventArgs from your custom thread as well, and capitalize on a common event object from any background worker...rather than try to deal with multiple types of progress reporting objects?

jrista
I don't know, good point. Although I think I'd end up running into the same sort of issue, as even if I restricted it to only ProgressChangedEventArgs there's nothing restricting what type is actually used for its UserState property.
Eric Anastas
Well, again, you can use a consistent data type in the UserState property, and use the 'as' keyword to safe cast to that type. If its null, you could ignore the event as an unknown progress event. Otherwise, you could use a single implementation for all progress events from any source.
jrista