Generalizing the interface to a progress bar in a GUI
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.Remove开发者_开发百科EventHandler(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?
I don't think the Progress Bar should be directly subscribed to the events.
It should implement an interface that exposes all the relavant functionality of the Progress Bar, this could handle cross thread calls making the control more thread safe. The internal code should be unaware of any external types, i.e. encapsulated.
The "wiring" should be in the common parent of the controls, mapping events to changes on the Progress Bar, via the public interface.
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?
精彩评论