Introduction
If your WinForms application needs to execute some long-running code, it's best to run it on a thread so the main thread of the form isn't hung up. It is also helpful if the form has a progress bar so the thread can let the user know how it's progressing. But this requires cross-thread access which needs to be done safely.
This brings up three points:
- The code for thread safety isn't that difficult, but why do have to write it for every form that needs a progress bar?
- The progress bar is only needed while the long-running code runs, so why have it on the form at all?
- What if the user wants to cancel the operation?
My solution is to use a dialog that encapsulates the thread-safe manipulation of the progress bar and allows any form to use it without having to add a progress bar and its code to the form itself.
The ability to cancel the operation depends on the operation, but is supported.
Using the code
The code for the ProgressDialog
is in ProgressDialog.cs, the other files are for the example application.
Because I derived from the Form
class, all the public methods and properties of Form
are available, which is not really a good idea. I should have encapsulated the form inside my class, but I decided to be lazy. The result is that the user of the class can modify the appearance of the dialog, and maybe that's not such a bad thing, you have the source code anyway.
At any rate, the only parts of the class that I expect the user of the class to use are:
- The constructors
- The
ShowDialog
method
- The
Result
property
- The
WasCancelled
property
- The
RaiseUpdateProgress
method
- The
ProgressDialogStart
delegate
The example demonstrates all of these but the delegate:
namespace Progger
{
public partial class Form1 : System.Windows.Forms.Form
{
PIEBALD.Dialogs.ProgressDialog dlg = null ;
public Form1 ()
{
InitializeComponent() ;
}
private void button1_Click ( object sender , System.EventArgs e )
{
System.DateTime start = System.DateTime.Now ;
dlg = new PIEBALD.Dialogs.ProgressDialog
(
"Sleeping",
this.Icon,
System.Windows.Forms.ProgressBarStyle.Blocks
true
delegate
(
object[] Params
)
{
int howlong = (int) Params [ 0 ] * 10 ;
for ( int runner = 0 ; !dlg.WasCancelled &&
( runner < howlong ) ; runner++ )
{
System.Threading.Thread.Sleep ( 100 ) ;
dlg.RaiseUpdateProgress ( runner * 100 / howlong ) ;
}
return ( System.DateTime.Now ) ;
},
(int) this.numericUpDown1.Value
) ;
dlg.ShowDialog() ;
this.label1.Text = string.Format
(
"{0} {1:0.00} seconds",
dlg.WasCancelled?"Woken after":"Slept for",
( (System.DateTime) dlg.Result - start ).TotalSeconds
) ;
}
}
}
Points of Interest
Things to learn from this: cross-thread safety, delegates, and anonymous methods.
History
- First posted - 2006-05-18.