Responsive UI With Time Taking Tasks
Responsive UI With Time Taking Tasks
Responsive UI With Time Taking Tasks
Thread t = new Thread (new Thread Start (TimeConsumingFunction));
t.Start();
Timer1.Start();
}
//Timer1 had a tick even associated Timer1_Tick
Timer1_Tick()
{
// Code to increment progress bar goes here..
}
But this dint work.
I tried to search on net but could'nt get any thing relevant as my problem was quite specific and unique.
I spent quite some time in going through some articles on Threading etc.
Finally I found 2 ways to achieve this and decided to put it here.......
1st Way
BackgroundWorker Component
When you want a responsive UI and you are faced with long delays associated with some time consuming operations, the BackgroundWorker component provides a convenient solution.
The BackgroundWorker is available in Tool Box and you can drag it on your form.
Now the BackgroundWorker class exposes few events. The ones that I used are...
DoWork
ProgressChanged
RunWorkerCompleted 1. DoWork
This gets invoked when you start the Worker (Thread). To start the worker you need to call the method RunWorkerAsync
Calling this function will invoke the DoWork event.
Your time consuming work needs to be done in the DoWork event.
The signature of this event is
DoWork(sender object, DoWorkEventArgs e)
Some important properties of DoWorkEventArgs
Argument - To pass an argument from RunWorkerAsync to DoWork event
Cancel - To state if the job is cancelled
Result - To store the result and make in available to other events.
We will see each with example.
Argument
Any arguments that you need to pass, can be passed through function RunWorkerAsync.
RunWorderAsync(101) // 101 is the argument I am passing.
The e.Argument will get this value
The event DoWork would be something like this....
Private void Backgroundworker1_DoWork(object sender, DoWorkEventArgs e)
{
ComputeFact((int)e.Argument) //This is similar to ComputeFact(101)
}
Cancel
There might be a requirement to cancel the timeconsuming task in middle.
For this you have to call a method CancelAsync()
When this method is called CancellationPending property of sender object is set to true.
This can be checked in DoWork event and e.Cancel should be set to true.
The same object 'e' of DoWorkEventArgs get carried to other events like RunWorkerCompleted and ProgressChanged and can be utilized there.
Result
This property can be used to store result in DoWork event.
This Result gets propagated to other events through the same object 'e'.
Summary of DoWork:- This event is raised when you call the RunWorkerAsync method. This is where you start the operation that performs the potentially time-consuming work.
Your code in the DoWork event handler should periodically check the CancellationPending property value and abort the operation if it is true. (By setting e.Cancel to true where e is object of type DoWorkEventArgs)
The result value should be stored in e.Result where e is object of type DoWorkEventArgs.
2. ProgressChanged.
This event is called each time the object sender calls the function ReportProgress
The reportProgress method accepts an integer argument which should be the percentage of progress made.
For using this the BackGroundWorker's ReportsProgress property should be set to true.
The Signature for this event is
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
e.ProgressPercentage - Will have the percentage reported by ReportProgress method
3. RunWorkerCompleted
This event gets fired when the Worker Thread gets completed i.e when the Thread finishes to execute the code in DoWork event.
The signature for this event is
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
e.Cancelled will have the Boolean value as true if e.Cancel is set true in DoWork event.
e.Result will contain the result.
e.Error will have the error no in case of any
As an example where the time consuming event is to compute a factorial is below:-
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace BackgroundWorkerPrj
{
public partial class Form1 : Form
{
long result = 1;
int percentprog, oldpercentprog;
public Form1()
{
InitializeComponent();
}
int orignalnum;
private void Form1_Load(object sender, EventArgs e)
{
InitializeBackgroundWorker();
orignalnum = 50;
backgroundWorker1.RunWorkerAsync((int)orignalnum);
}
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork+=new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged+=new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.WorkerReportsProgress = true;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker w = sender as BackgroundWorker;
e.Result=computeFactorial((int)e.Argument,w,e);
}
private long computeFactorial(int s,BackgroundWorker w,DoWorkEventArgs e)
{
Thread.Sleep(100);
if (w.CancellationPending)
e.Cancel = true;
else
{
//To caluclate the progress percentage.
percentprog = ((orignalnum - s) * 100) / orignalnum;
//If there is any progress than last iteration...
if (percentprog > oldpercentprog)
{
oldpercentprog = percentprog;
w.ReportProgress(percentprog);
}
if (s == 1)
result= 1;
else
result = result * s*computeFactorial(s - 1, w, e);
}
return result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
if (e.Cancelled == true)
{
label1.Text = "Cancelled";
}
else
{
label1.Text = e.Result.ToString();
}
}
else
label1.Text = "Error Occured";
}
}
}
Limitation :-
This component does not exist in .Net 1.1. So the 2nd way would work there.
2nd Way
Creating Threads.
This is a simple way of creating threads. Just that the time consuming operation should be made a background thread by using the attribute IsBackground = true.
So the code would look like this
{
Thread t = new Thread (new Thread Start (TimeConsumingFunction));
t.IsBackGround=true;
t.Start();
Timer1.Start();
}
//Timer1 had a tick even associated Timer1_Tick
Timer1_Tick()
{
// Code to increment progress bar goes here..
Thread.Sleep(100);
}
Deepa Lakhani
deepalakhani@yahoo.com
Pretty Little Liars Episode on ABC State of 1031 Forensic Accounting Courses On The Web Protect your Ears from accumulation of Fluid in ear Bubbling it up with the seltzer maker What next after 12th They Say She's Confused Testking 70-667 Basics Associated With Forensic Accounting Mcitp 70-667 How to buy vast hunting lands in the State of Missouri Prayer Activates Power In The Life Of A Believer Incentives Obtained By Forensic Accountants