Wow its been a long time since I wrote a Windows Form application, I think I already forgot how to develop one as I now default all development to Web MVC format.
Several days ago I was tasked to do a bulk copy of files with some smarts embedded to it as we are migrating some File Servers, at first I was thinking why not just copy paste or buy a third-party product? Sadly that can’t happen as there are some business logic that needs to be implemented as we copy the files so as a quick and easy way to implement this I decided I create a Windows Form application. At first I thought of creating a simple console application but I wanted to give a decent UI so I can report on its progress to the one who will be using them, so Win Forms it is. At first I directly coded everything in the button click event and refresh the labels as it changed, having said that the UI was totally unresponsive, it looks like the application had frozen, I get the (Not Responding) message and I cannot drag my window around, while I know it still works at the back it is not good as you don’t really know whats the progress.
Then suddenly I remember I have to separate the heavy processing to another thread from the UI for it to become responsive so I looked at my old snippet library and found this. To separate both the UI thread to the Process thread you just need to use a BackgroundWorker and give it the load relieving the UI thread of the heavy process.
To use it is as simple as calling
backgroundWorker1.RunWorkerAsync();
And perform your intensive process on the DoWork Event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
Now how do you report on its progress? Its as simple as calling the ReportProgress like such
BackgroundWorker worker = sender as BackgroundWorker; string[] CurrentStatus = new string[1]; CurrentStatus[0] = "Your Message Here"; worker.ReportProgress(YourPercentageHere, CurrentStatus);
Which in turn calls the event ProgressChanged
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
And from here you can increment your progress bar
progressBar1.Value = e.ProgressPercentage;
and also updates any labels that you might be using like such
string[] StatusMsg = (string[])e.UserState; yourLabel.Text = StatusMsg[0];
Now how do you cancel the processing if needed? Well again its as simple as calling CancelAsync
backgroundWorker1.CancelAsync();
This triggers a cancel task where you can test while you do your intensive task by doing something like this
if (worker.CancellationPending == true) { e.Cancel = true; break; }
Which then calles the Event RunWorkerCompleted
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
Once you’ve done this correctly the application form window will run butter smooth and the progress will be reported to the UI in a very responsive manner. To give more clarity I will give you a code sample below but please take note I removed some parts of it for brevity.
The main intention of the form below is to perform a copy paste of a whole large directory to another location and will demonstrate running this intensive process in a separate thread so UI will run smooth without hanging.
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private IEnumerable<DirectoryInfo> varyLargeWindowsDirectory = new DirectoryInfo("{Your Source Directory}") .EnumerateDirectories("*", SearchOption.TopDirectoryOnly) .Where(x => !x.Name.EndsWith(SystemSettings.CopyDirectorySuffix())); private string[] CurrentStatus = new string[2]; private string sourceFileDirectory = "{Your Source File Directory}"; private string destinationFileDirectory = "{Your Destination File Directory}"; private void Form1_Load(object sender, EventArgs e) { progressBar1.Hide(); //Instansiate Background Worker Thread backgroundWorker1.WorkerReportsProgress = true; backgroundWorker1.WorkerSupportsCancellation = true; } private void startAsyncButton_Click(object sender, EventArgs e) { if (backgroundWorker1.IsBusy != true) { progressBar1.Show(); startAsyncButton.Enabled = false; cancelAsyncButton.Enabled = true; //Start the Asynchronous Operation. backgroundWorker1.RunWorkerAsync(); } } private void cancelAsyncButton_Click(object sender, EventArgs e) { if (backgroundWorker1.WorkerSupportsCancellation == true) { // Cancel the Asynchronous Operation. backgroundWorker1.CancelAsync(); } } // This event handler is where the intensive process is performed. private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; float totalItems = (float)varyLargeWindowsDirectory.Count(); float itemCounter = 0; foreach (DirectoryInfo currentDirectory in varyLargeWindowsDirectory) { if ((worker.CancellationPending == true)) { e.Cancel = true; break; } itemCounter = itemCounter + 1; float percentage = itemCounter / totalItems; CurrentStatus[0] = "Processing " + currentDirectory.Name; worker.ReportProgress((int)(percentage * 100), CurrentStatus); //Copy all the files & Replaces any files with the same name foreach (string sourceFilePath in Directory.GetFiles(currentDirectory.FullName, "*.*", SearchOption.AllDirectories)) { CurrentStatus[1] = "Copying " + sourceFilePath; worker.ReportProgress((int)(percentage * 100), CurrentStatus); File.Copy(sourceFilePath, sourceFilePath.Replace(sourceFileDirectory, destinationFileDirectory), true); } worker.ReportProgress((int)(percentage * 100), CurrentStatus); } MessageBox.Show("Processing Stopped or Finished, Application will now close"); Application.Exit(); } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Update the Progress Bar and Labels on Progress Report if (typeof(string[]) == e.UserState.GetType()) { string[] StatusMessage = (string[])e.UserState; if (2 == StatusMessage.GetLength(0)) { // Update the Status lLabels lblDirectoryStatus.Text = StatusMessage[0]; lblFileStatus.Text = StatusMessage[1]; } } // Update Progress Bar Percentage this.progressBar1.Value = e.ProgressPercentage; } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // Background Worker Cancelled if ((e.Cancelled == true)) { lblDirectoryStatus.Text = "Canceled!"; MessageBox.Show("Processing Cancelled, Application will now close", "Cancelled"); } // Background Worker Error else if (!(e.Error == null)) { lblDirectoryStatus.Text = ("Error: " + e.Error.Message); } else { lblDirectoryStatus.Text = "Done!"; } } }
Its as easy as that! Happy Coding