Updating UI from a background thread which is called in a loop in main UI when the thread finishes
multithreading, winforms
Solution
Add a Winforms Project, Drop a Label Control on the Form , Copy-Paste this code and Hit F5
[EDIT]: Updated with the `business class` comment from the user
NB: My form class is named `Form3`. You may have to change your `Program.cs or vice-versa.`
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
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);
}
}
}
With this you can achieve Cancel functionality also very easily. Observe that during initialisation, I set the `WorkerSupportsCancellation = true` & then I check for `_worker.CancellationPending` in the DoWork. So, if you want to cancel the process by a `Cancel Button click`, then you will write this code in the button handler- `_worker.CancelAsync();`
Problem
I have a WinForms application that is calling a business class method that performs some heavy duty action taking about 5 seconds for each call. The main form calls this method in a loop. This loop can run from 10 times to maybe up to 10 thousand times. The WinForms application sends a parameter to the business class and has an area to display the time taken for each method call and what the value returned by the method. How do I inform my main window and update a text area in the main winform with what the method has returned for each call? Currently the data comes all at once after all the threads have finished. Is there a way to update the UI for all the iterations of the loop once the each call is done? I don't mind if it is done sequentially also. The FORM ``` HeavyDutyClass hd; public Form1() { InitializeComponent(); hd = new HeavyDutyClass(); } //BUTTON CLICK private void Start_Click(object sender, EventArgs e) { int filecount = 5000; //BAD - opening 5000 threads! Any other approach? hd.FileProcessed += new EventHandler(hd_FileProcessed); var threads = new Thread[filecount]; for (int i = 0; i < filecount; i++) { threads[i] = new Thread(() => { hd.LongRunningMethod(); }); threads[i].Start(); } } //BUSINESS CLASS EVENT THAT FIRES WHEN BUSINESS METHOD COMPELTES void hd_FileProcessed(object sender, EventArgs e) { if (dgv.InvokeRequired) { dgv.Invoke((MethodInvoker)delegate { UpdateGrid(); }); } } private void UpdateGrid() { dgv.Rows.Add(1); int i = dgv.Rows.Count; dgv.Rows [ i-1].Selected = true; dgv.FirstDisplayedScrollingRowIndex = i - 1; } ``` The business HeavyDuty class ``` public event EventHandler FileProcessed; public HeavyDutyClass() { } protected virtual void OnMyEvent(EventArgs e) { if (FileProcessed != null) { FileProcessed(this, e); } } public bool LongRunningMethod() { for (double i = 0; i < 199990000; i++) { //time consuming loop } OnMyEvent(EventArgs.Empty); return true; } ```