I have some process heavy tasks that run in my WinForms app. The problem is, while its running, it freeze the UI (UI main thread).
I haven't worked that much with threads and delegates in C# yet, and that's why I hope someone could help me to, how to handle those process heavy tasks, without freezing the UI, so the user don't think the app is crashing while waiting?
Eg. I have a call through my FrontController, that takes time:
_controller.testExportExcel(wrapper, saveDialog.FileName);
Since it's creating an Excel file. I won't the app to be responding on the UI while its working.
Another example of a process heavy task could be this:
private void dataGridView_liste_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
if (e.ListChangedType != ListChangedType.ItemDeleted)
{
foreach (DataGridViewRow r in dataGridView_liste.Rows)
{
DataGridViewCellStyle red = dataGridView_liste.DefaultCellStyle.Clone();
red.BackColor = Color.LightGreen;
if (r.Cells["News"].Value != null && (bool)r.Cells["News"].Value == true)
r.DefaultCellStyle = red;
}
}
}
Where the foreach loop takes time, and freeze the UI. An async thread running the process and automatically closing when its done, could be useful I think. But how does it work??
How about using a Task (if targetting .net 4)? This is considered as a replacement of the BackgroundWorker class since it supports nesting (parent/child tasks), task continuations, etc.
E.g.
private void dataGridView_liste_DataBindingComplete(object sender,
DataGridViewBindingCompleteEventArgs e)
{
Task t = Task.Factory.StartNew(() =>
{
// do your processing here - remember to call Invoke or BeginInvoke if
// calling a UI object.
});
t.ContinueWith((Success) =>
{
// callback when task is complete.
}, TaskContinuationOptions.NotOnFaulted);
t.ContinueWith((Fail) =>
{
//log the exception i.e.: Fail.Exception.InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);
}
I answered a very similar question here
It boils down to using BackgroundWorker.
msdn provides an example:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SL_BackgroundWorker_CS
{
public partial class Page : UserControl
{
private BackgroundWorker bw = new BackgroundWorker();
public Page()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
{
bw.CancelAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.tbProgress.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.tbProgress.Text = ("Error: " + e.Error.Message);
}
else
{
this.tbProgress.Text = "Done!";
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
}
}
Everything that runs in the DoWork event handler is asynchronous.
Everything that runs in ProgessChanged/RunWorkCompleted's event handlers is on the UI thread.
For your first example, a call to _controller.testExportExcel(), a BackgroundWorker or Task Parallel Library call (i.e. Task.Factory.StartNew(...)) would be appropriate to satify your requirement of keeping the UI responsive. Plenty of examples floating around, including the other answers here.
For your second example, you will find you can't put this on a background thread since it appears to be code that manipulates the UI. Specifically, if the implementation of your BackgroundWorker's DoWork event handler, or the delegate you pass to Task.Factory.StartNew(), or the method for a plain old thread touch the UI, you are highly likely (/certain?) to get an exception stating "Cross-thread operation not valid".
The reason for this is covered in this question. But I'm more surprised actually this is slow enough that you want to make it asynchronous. There might be some simple ways to make this code more responsive - Control.SuspendLayout() and .ResumeLayout() springs to mind.
Related
Good morning, I'm trying to write an application that use in his interface a progressbar (in C#, WPF). I have read about the need of perform the UI task in a different thread, using Backgroundworker. I trying to make it work using a lot of information, but nothing happens (the program work fine, but the progressbar only shown at the end of the "hard-work tasks").
I'm civil engineer (not a software one), so I ask if anyone can help me with that.
namespace SAP2000___Quake_Definitions
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly BackgroundWorker bgWoker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
this.bgWoker.WorkerReportsProgress = true;
this.bgWoker.WorkerSupportsCancellation = true;
this.bgWoker.DoWork += bgWorker_DoWork;
this.bgWoker.ProgressChanged += bgWorker_ProgressChanged;
this.bgWoker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progBar.Value = e.ProgressPercentage;
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgWorker = (BackgroundWorker)sender;
Dispatcher.Invoke(new Action(() => DoTheHardWork()));
}
private void processButton_Click(object sender, RoutedEventArgs e)
{
this.bgWoker.RunWorkerAsync();
}
private void DoTheHardWork()
{
switch (this.chckBox2.IsChecked.GetValueOrDefault())
{
case true:
this.bgWoker.ReportProgress(0);
//more hardwork with inputs from WPF
case false:
this.bgWoker.ReportProgress(0);
//more hardwork with inputs from WPF
}
}
}
}
That is not how you should be using a BackgroundWorker. I wrote some example code a few years back. It should get you on the right track:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
You have to limit all UI writing work to the Progress Report and Run wokrer compelte Events. Those will be raised in the thread that created the BGW (wich should be the UI thread) automagically.
Note that you can only report progress between distinct steps. I had the advantage that I had to write the loop anyway. But if you have existing code (like most download or disk code), you can usually only report between files.
my mistakes were three:
Trying to use "Dispatcher.Invoke(new Action(() => DoTheHardWork()));" to solve an exception related to my thread (exception caused by point #3).
Avoiding the instantiation: BackgroundWorker bgWorker = (BackgroundWorker)sender (thank you #Christopher).
Writing a code that manipulate a UI-Component inside the DoWork event handle of my Backgroundworker. MSDN says: You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events. Trying this, the exception occur.
Solving the point #2 and #3, the UI is perfectly responsive respect to the "hardwork" function (runned in background).
I am having a MDI form, now clicking on the menu in MDI form, I am opening other forms in PnlView (Panel), this acts as container for forms.
What I am thinking is, when the application loads, it opens a default form.
At this point I want to open all forms, but as opening all forms at a time will hamper the performance, other forms opening should run in different thread. Then if all forms are open then user can switch between forms quickly.
private bool IsFormAlreadyOpen(ControlItem _item)
{
bool reutrnValue = false;
foreach (Control ctrl in PnlView.Controls)
{
if (ctrl.Name.ToLower() == _item.Control.Name.ToLower())
{
reutrnValue = true;
break;
}
}
return reutrnValue;
}
This is the function which checks if form is already opened or not. So this will get all the forms open and will just bring that form to front.
Can anyone helps me with opening form in thread so that it will not impact the performance.
I hope this makes sense.
---------------------------------------------------------------------
private void MdiForm1_Load(object sender, EventArgs e)
{
OpenFirstForm(); //This ospens a default form, form name frmDefault
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 2); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
//System.Threading.Thread.Sleep(500);
OpenForms(form1);
OpenForms(form2);
worker.ReportProgress((i * 10));
}
}
}
It throws an exception,
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on
Getting below exception,
No Overload 'OpenForm' matches delegate MethodInvoker
this.Invoke(new MethodInvoker(OpenForm),new object[] { Keys.F12, Keys.Alt });
private void OpenForm(Keys keyPressed, Keys modifier)
{
---------------------------
}
The problem is that you are trying to access a control instantiated in one thread from another thread. Use the following code snippet to solve your issue at hand:
// Paste this snippet in the beginning of your method
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(/*Enter the name of your method here*/));
return;
}
// Method code goes here ......
/*
Example:
private void SomeMethod(object sender, EventArgs e) {
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(SomeMethod)); // Name of current method is 'SomeMethod'
return;
}
// Code continues here
int x,y,z;
// Do something .....
}
*/
Hope this helps.
I have a window with a calendar and datagrid.
When the user selects a new date in the calendar I want to query the database for calls made on that date.
public HistoryDialog()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
HistoryGrid.SelectionChanged += new SelectionChangedEventHandler(HistoryGrid_SelectionChanged);
}
void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
startDate = calendar.SelectedDates.OrderBy(x => x.Date).FirstOrDefault();
endDate = calendar.SelectedDates.OrderByDescending(x => x.Date).FirstOrDefault();
if (endDate != startDate)
SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate) + " - " + String.Format("{0:d MMMM}", endDate);
else
SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate);
SearchInDatabase();
}
private void SearchInDatabase()
{
if (worker.IsBusy)
worker.CancelAsync();
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
return;
}
var CallLog = ct.GetCalllogs(startDate, endDate, userID); // Database query
e.Result = CallLog;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IList CallLog = e.Result as IList;
foreach (CalllogInfo callInfo in CallLog)
{
chvm.CallHistoryList.Add(callInfo);
}
}
But when the user selects a new date while the backgroundworker is still running my program crashes.
How can I stop a running background worker and start a new one ?
Now I see you have set the WorkerSupportsCancellation property to true.
Now this doesnt actually cancel your BackgroundWorker it simply allows you to call the CancelAsync() method.
What you need to do is in your method processing periodically check to ensure the working is not pending cancellation from the CancellationPending property. As you check this property when you find it true you can set the Cancel property of the event arguments to true. This will then be available in your RunWorkerCompleted event. At this point (in your RunWorkerCompleted event handler) you can then restart the BackgroundWorker.
Here is an example using very basic background worker that supports cancellation and responds to the cancel request.
public MainWindow()
{
InitializeComponent();
this.DataContext = dataModel;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += (o, e) =>
{
//do a long running task
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(500);
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
worker.RunWorkerCompleted += (o, e) =>
{
if (e != null && e.Cancelled)
{
startTheWorker();
return;
}
//TODO: I AM DONE!
};
}
BackgroundWorker worker;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (worker != null && worker.IsBusy)
worker.CancelAsync();
else if(worker != null && !worker.CancellationPending)
startTheWorker();
}
void startTheWorker()
{
if (worker == null)
throw new Exception("How come this is null?");
worker.RunWorkerAsync();
}
As you can see we are having to do all the work of actually cancelling the worker. Hope this helps.
In addition to the other answers, I want to add the following. The following block of code is responsible for raising the error:
private void SearchInDatabase()
{
if (worker.IsBusy)
worker.CancelAsync();
worker.RunWorkerAsync();
}
As you call CancelAsync, execution of your code is continued immediately without waiting until the BackgroundWorker is really stopped. This is the reason why RunWorkerAsync is called while the BackgroundWorker is still running resulting in the error you describe. You should change your code as follows:
private void SearchInDatabase()
{
if (worker.IsBusy)
worker.CancelAsync();
while(worker.IsBusy)
System.Threading.Thread.Sleep(100);
worker.RunWorkerAsync();
}
This assures that the BackgroundWorker finishes its work before starting a new one. In addition, you need to enhance your DoWork-method to check the CancellationPending property more often in order to really stop the BackgroundWorker soon after a cancellation request.
Your problem is coming from the cancellation.
When you are in ct.GetCalllogs your code is blocked and doesn't support cancellation.
In ct.GetCalllogs you should verify that the backgroundworker isn't in cancel state and then start again with the new value.
Here is an element of response how to stop backgroundworker correctly
Disallowing selections while the worker is doing work would remove the issue without the need to tackle it:
void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
// .. cut ..
// disable the calendar and go on
calendar.IsEnabled = false;
SearchInDatabase();
}
private void SearchInDatabase()
{
// No longer needed: selection processing is now "atomic"
// if (worker.IsBusy) worker.CancelAsync();
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// .. cut ..
calendar.IsEnabled = true;
}
Or you could bind worker.IsBusy to calendar.IsEnabled to have it handled "automagically" (while still not having to worry about concurrent executions of the worker method).
In my application I need to perform a series of initialization steps, these take 7-8 seconds to complete during which my UI becomes unresponsive. To resolve this I perform the initialization in a separate thread:
public void Initialization()
{
Thread initThread = new Thread(new ThreadStart(InitializationThread));
initThread.Start();
}
public void InitializationThread()
{
outputMessage("Initializing...");
//DO INITIALIZATION
outputMessage("Initialization Complete");
}
I have read a few articles about the BackgroundWorker and how it should allow me to keep my application responsive without ever having to write a thread to perform lengthy tasks but I haven't had any success trying to implement it, could anyone tell how I would do this using the BackgroundWorker?
Add using
using System.ComponentModel;
Declare Background Worker:
private readonly BackgroundWorker worker = new BackgroundWorker();
Subscribe to events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
Run worker async whenever your need.
worker.RunWorkerAsync();
Track progress (optional, but often useful)
a) subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork
b) set worker.WorkerReportsProgress = true; (credits to #zagy)
You may want to also look into using Task instead of background workers.
The easiest way to do this is in your example is Task.Run(InitializationThread);.
There are several benefits to using tasks instead of background workers. For example, the new async/await features in .net 4.5 use Task for threading. Here is some documentation about Task
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task
using System;
using System.ComponentModel;
using System.Threading;
namespace BackGroundWorkerExample
{
class Program
{
private static BackgroundWorker backgroundWorker;
static void Main(string[] args)
{
backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += backgroundWorker_DoWork;
//For the display of operation progress to UI.
backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
//After the completation of operation.
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync("Press Enter in the next 5 seconds to Cancel operation:");
Console.ReadLine();
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
Console.ReadLine();
}
}
static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 200; i++)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
return;
}
backgroundWorker.ReportProgress(i);
Thread.Sleep(1000);
e.Result = 1000;
}
}
static void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine("Completed" + e.ProgressPercentage + "%");
}
static void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
Console.WriteLine("Operation Cancelled");
}
else if (e.Error != null)
{
Console.WriteLine("Error in Process :" + e.Error);
}
else
{
Console.WriteLine("Operation Completed :" + e.Result);
}
}
}
}
Also, referr the below link you will understand the concepts of Background:
http://www.c-sharpcorner.com/UploadFile/1c8574/threads-in-wpf/
I found this (WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI. link) to contain the rest of the details which are missing from #Andrew's answer.
The one thing I found very useful was that the worker thread couldn't access the MainWindow's controls (in it's own method), however when using a delegate inside the main windows event handler it was possible.
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
// Get a result from the asynchronous worker
T t = (t)args.Result
this.ExampleControl.Text = t.BlaBla;
};
I have a simple app which fires of a series of data intensive tasks. I'm not very experienced with WinForms and I was wondering the best way to do this without locking the interface. Can backgroundWorker be re-used, or is there another way to do this?
Thanks
BackgroundWorker is a thread that also includes notification synchronization. For example, if you wanted to update your UI when the scan completes, a regular Thread cannot access the UI objects (only the UI thread can do that); so, BackgroundWorker provides a Completed event handler that runs on the UI thread when the operation completes.
for more info see: Walkthrough: Multithreading with the BackgroundWorker Component (MSDN)
and a simple sample code:
var worker = new System.ComponentModel.BackgroundWorker();
worker.DoWork += (sender,e) => Thread.Sleep(60000);
worker.RunWorkerCompleted += (sender,e) => MessageBox.Show("Hello there!");
worker.RunWorkerAsync();
backgroundWorker can be used.
its benefit - it allows you to update a progress bar and interact with UI controls. (WorkerReportsProgress)
Also it has a cancellation mechanism. (WorkerSupportsCancellation)
You can use BackgroundWorker for such requirements. Below is a sample which updates a label status based on percentage task [long running] completion. Also, there is a sample business class which sets some value and the value is set back to UI via ProgressChanged handler. DoWork is the place where you write your long running task logic. Copy-Paste the code below after adding a label and backgroundworker component on a Winforms app & give it a shot. You may debug across various handler [RunWorkerCompleted, ProgressChanged, DoWork] and have a look at InitWorker method. Notice the cancellation feature too.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
The background worker would be a good choice to start with
For more info look here
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx