backgroundworker class in WPF - c#

I am learning backgroundworker class in WPF. The code below is in file MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace FrontEnd
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker backGroundWorker;
public MainWindow()
{
InitializeComponent();
backGroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker"));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
Flow pro = new Flow(20,10);
backGroundWorker.RunWorkerAsync(pro);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Flow pro = (Flow)e.Argument;
e.Result = pro.NaturalNumbers();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value= e.ProgressPercentage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((int)e.Result == 1) MessageBox.Show("DONE");
progressBar1.Value = 0;
}
}
}
The code below is in file Flow.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace FrontEnd
{
class Flow
{
long i;
//private int x,y;
public int X
{
get; set;
}
public int Y
{
get; set;
}
public Flow(int x, int y)
{
X = x;
Y = y;
}
public int NaturalNumbers()
{
for (i = 0; i < 9999; i++)
{
Console.WriteLine(i);
long iteration = i * 100 / 9999;
if ((i % iteration == 0) &&
(backgroundWorker != null) && backgroundWorker.WorkerReportsProgress)
{
backgroundWorker.ReportProgress(iteration);
}
}
return 1;
}
}
}
Error : The name 'backgroundWorker' does not exist in the current
context
How can I make progress bar working?

Here's a simple example that works:
public partial class BackgroundWorkerPage : Page
{
private readonly BackgroundWorker _worker = new BackgroundWorker();
public BackgroundWorkerPage()
{
InitializeComponent();
_worker.DoWork += WorkerOnDoWork;
_worker.WorkerReportsProgress = true;
_worker.ProgressChanged += WorkerOnProgressChanged;
}
private void WorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
progressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(50);
_worker.ReportProgress(i);
}
}
private void Button_Click_1(object sender, System.Windows.RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ProgressBar x:Name="progressBar" Height="23" Minimum="0" Maximum="100"/>
<Button Grid.Row="1" Height="23" Content="Start" Click="Button_Click_1"/>
</Grid>
And you need to change your code a bit
private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
var flow = new Flow(_worker);
flow.NaturalNumbers();
}
internal class Flow
{
private readonly BackgroundWorker _worker;
public Flow(int x, int y)
{
X = x;
Y = y;
}
public Flow(BackgroundWorker worker)
{
_worker = worker;
}
public int X { get; set; }
public int Y { get; set; }
public int NaturalNumbers()
{
for (int i = 0; i <= 9999; i++)
{
int iteration = i*100/9999;
// your if(...) fails with divide by zero exception
_worker.ReportProgress(iteration);
}
return 1;
}
}

Introduction
Whenever we try to do some long running operation on the UI without freezing it, we need to run it in a separate thread.In this article we will look into the BackgroundWorker class , as one of the various solutions to this problem with a simple example. BackgroundWorker executes an operation on a separate thread and provide a notification to UI Thread whenever necessary.
Straight To Experiment
Let us create a UI as under
The objective is that,when we will click on the "Populate" button, at the same time we should be able to write something in the "Textbox".
Now let us look into the code which is without BackgroundWorker
public partial class WithOutBackgroundThread : Form
{
List<Employee> lstEmp;
public WithOutBackgroundThread()
{
InitializeComponent();
lstEmp = new List<Employee>();
}
private void btnPopulate_Click(object sender, EventArgs e)
{
GetEmployeeRecords();
dataGridView1.DataSource = lstEmp;
lblStatus.Text = "Work Completed";
}
//Prepare the data
private void GetEmployeeRecords()
{
for (int i = 1; i <= 10; i++)
{
// Simulate a pause
Thread.Sleep(1000);
lstEmp.Add(new Employee { EmpId = i, EmpName = "Name" + i });
}
}
}
The code is pretty straightforward.In the "GetEmployeeRecords()" method, we are preparing the data.We have introduce the "Thread.Sleep(1000)" to make a delay. And in the "Populate" button click event, we are populating the Gird.
But if we execute this code, the UI will become unresponsive and henceforth, we cannot perform any task on the "Textbox" which is our objective.
Henceforth let us change our code to the below
public partial class WithBackgroundThread : Form
{
BackgroundWorker workerThread;
List<Employee> lstEmp;
public WithBackgroundThread()
{
InitializeComponent();
lstEmp = new List<Employee>();
workerThread = new BackgroundWorker();
workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
}
private void btnPopulate_Click(object sender, EventArgs e)
{
workerThread.RunWorkerAsync();
}
private void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
GetEmployeeRecords();
}
private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblStatus.Text = "Work Completed";
dataGridView1.DataSource = lstEmp;
}
//Prepare the data
private void GetEmployeeRecords()
{
for (int i = 1; i <= 10; i++)
{
// Simulate a pause
Thread.Sleep(1000);
lstEmp.Add(new Employee { EmpId = i, EmpName = "Name" + i });
}
}
}
A lot of new things.We will explore one by one.
First, we need to declare BackgroundWorker Thread
BackgroundWorker workerThread = new BackgroundWorker();
Next,we need to subscribe to the events
workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
As a third step, we need to implement the two methods
private void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
GetEmployeeRecords();
}
private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//update ui once worker complete its work
lblStatus.Text = "Work Completed";
dataGridView1.DataSource = lstEmp;
}
The "DoWork" event is raised when we call the "RunWorkerAsync" method. This is where we start the operation that performs the potentially time-consuming work.The "RunWorkerCompleted" event is fired when the background operation has completed, has been canceled, or has raised an exception
As a last step, invoke the "RunWorkerAsync" from the "Populate" button click event.
private void btnPopulate_Click(object sender, EventArgs e)
{
workerThread.RunWorkerAsync();
}
The "RunWorkerAsync" starts execution of a background operation.
Now if we run our application we will be able to populate the grid as well as write something on the "Textbox".
Thanks

Related

c# multiple lock for multiple events

I'm trying to understand the lock mechanism.
if I have multiple events to lock on different value should I use an object lock for each?
More serious code added:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace ValueChangeOnEventForm
{
public partial class Form1 : Form
{
private Test_Onchange DataSource;
Thread Task1;
private bool Flag_Stop_Task1;
public Form1()
{
InitializeComponent();
graph1.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph1.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph1.ChartAreas[0].AxisX.ScaleView.Size = 100;
graph2.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
graph2.ChartAreas[0].AxisX.IsLabelAutoFit = true;
graph2.ChartAreas[0].AxisX.ScaleView.Size = 100;
DataSource = new Test_Onchange();
DataSource.ValueChanged += new EventHandler(EventValueChange);//Value input info
DataSource.SecondValueChange += new EventHandler(EventSecondValueChange);//second value
Task1 = new Thread(new ThreadStart(Task_1));//create the thread
Task1.Start();//start the thread
}
protected virtual void EventSecondValueChange(object sender, EventArgs e)
{
double valueMAX = 0, size = 0;
if (graph1.InvokeRequired)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.AddY(DataSource.Value); }));
graph1.Invoke(new MethodInvoker(delegate { valueMAX = graph1.ChartAreas[0].AxisX.Maximum; }));
graph1.Invoke(new MethodInvoker(delegate { size = graph1.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph1.Invoke(new MethodInvoker(delegate { graph1.ChartAreas[0].AxisX.ScaleView.Scroll(graph1.ChartAreas[0].AxisX.Maximum); }));
graph1.Invoke(new MethodInvoker(delegate { graph1.Series["ValueOnGraph"].Points.RemoveAt(0); }));
}
}
}
protected virtual void EventValueChange(object sender, EventArgs e)
{
double valueMAX=0,size=0;
if (graph2.InvokeRequired)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.AddY(DataSource.Secondvalue); }));
graph2.Invoke(new MethodInvoker(delegate { valueMAX = graph2.ChartAreas[0].AxisX.Maximum; }));
graph2.Invoke(new MethodInvoker(delegate { size = graph2.ChartAreas[0].AxisX.ScaleView.Size; }));
if (valueMAX - 10 > size)
{
graph2.Invoke(new MethodInvoker(delegate { graph2.ChartAreas[0].AxisX.ScaleView.Scroll(graph2.ChartAreas[0].AxisX.Maximum); }));
graph2.Invoke(new MethodInvoker(delegate { graph2.Series["ValueOnGraph2"].Points.RemoveAt(0); }));
}
}
}
private void Task_1()
{
while (!Flag_Stop_Task1)
{
Random RandVal = new Random();
Random RandVal2 = new Random();
int Value = RandVal.Next(0, 100);
int SecondValue = RandVal2.Next(50, 200);
DataSource.Value = Value;
DataSource.Secondvalue = SecondValue;
Thread.Sleep(100);
}
Flag_Stop_Task1 = false;
}
private void btn_StopTask_1_Click(object sender, EventArgs e)
{
Flag_Stop_Task1 = true;
}
}
}
And then
namespace ValueChangeOnEventForm
{
class Test_Onchange
{
private int value;
private int secondvalue;
protected object _lock = new object();
public event System.EventHandler ValueChanged;
public event System.EventHandler SecondValueChange;
protected virtual void OnValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = ValueChanged;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
protected virtual void OnSecondValueChange()
{
lock (this._lock)
{
EventHandler eventvaluechange = SecondValueChange;
if (eventvaluechange != null)
eventvaluechange(this, EventArgs.Empty);
}
}
public int Value
{
get { return this.value; }
set
{
if (value != this.value)
{//if value changed enter
this.value = value;
OnValueChange();
}
}
}
public int Secondvalue
{
get { return this.secondvalue; }
set
{
if (value != this.secondvalue)
{//if value changed enter
this.secondvalue = value;
OnSecondValueChange();
}
}
}
}
}
Do I need two lock (lock1 and lock2 object or only one for both value and secondvalue....?
Thanks a lot.
Update
Ok let's do it so.
I'm using beckhoff PLC which are real time Task PLC. and I'm reading two value on it when the value change. like this:
Form1 Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Error ID and Force change showed in Form1 label lv_ErrorID and lv_Force and graphForceIN add point.
The events handler on the other side (PLC class) looks like this:
PLC Class:
namespace RealTimeLock
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public partial class Form1 : Form
{
private PLC PLCData;
public Form1()
{
InitializeComponent();
}
public Form1(IBAAdsServer _adsServer)
: this()
{
PLCData = new PLC(_adsServer);
PLCData.ErrorBoolChanged += new EventHandler(EventErrorChanged);//error info
PLCData.ForceValChanged += new EventHandler(EventForceChanged);//Force input info
}
protected virtual void EventErrorChanged(object sender, EventArgs e)
{
//state of error PLC
lv_ErrorInfo.Text = "PLC Error num : " + PLCData.i_ErrorID.ToString();
}
protected virtual void EventForceChanged(object sender, EventArgs e)
{//modify graphical data PLC Force data
lv_ForceInfo.Text = PLCData.i_ForceVal.ToString();
c_graphForceIN.Series["ForceData"].Points.AddY(PLCData.i_ForceVal);
if (c_graphForceIN.ChartAreas[0].AxisX.Maximum - 10 > c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Size)
{
c_graphForceIN.ChartAreas[0].AxisX.ScaleView.Scroll(c_graphForceIN.ChartAreas[0].AxisX.Maximum);
c_graphForceIN.Series["ForceData"].Points.RemoveAt(0);
}
}
}
}
Does it seem to be correct coding for you guys? and while I have a real time task running there do I need to lock variables and if so, do I need two lock or only one??
Thanks for your remark on this!!

C# BackgroundWorker class

Currently im trying to update my progress bar if the background worker reports something, heres my code
Form1.cs
namespace YTD
{
public partial class Form1 : Form
{
private Main app;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
int n;
bool isNumeric = int.TryParse(numberBox.Text, out n);
if (!String.IsNullOrWhiteSpace(emailBox.Text) && !String.IsNullOrWhiteSpace(passBox.Text) && !String.IsNullOrWhiteSpace(numberBox.Text) && isNumeric)
{
this.app = new Main(emailBox.Text, passBox.Text, n, logBox, statusBar, backgroundWorker1);
this.app.startMule();
}
else
{
MessageBox.Show("Please fill out all the form fields", "MuleMaker error");
}
}
}
}
And my Main.cs
namespace YTD.classes
{
public class Main
{
private String email;
private String password;
private int number;
private RichTextBox logBox;
private ProgressBar statusBar;
private BackgroundWorker threadWorker;
public Main(String email, String password, int number, RichTextBox logBox, ProgressBar statusBar, BackgroundWorker threadWorker)
{
// Define some variables
this.email = email;
this.password = password;
this.number = number;
this.logBox = logBox;
this.statusBar = statusBar;
this.threadWorker = threadWorker;
}
public void startMule()
{
// Set progressbar 100% value
statusBar.Maximum = this.number;
if (!threadWorker.IsBusy)
{
threadWorker.RunWorkerAsync();
}
}
private void threadWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
// Perform a time consuming operation and report progress.
MessageBox.Show("ye");
System.Threading.Thread.Sleep(500);
threadWorker.ReportProgress(i * 10);
}
}
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Increment(1);
}
}
}
Currently I get no errors but the progress bar value is not beeing changed.
Without the background worker i can update my progress bar fine but not while doing an expensive action.
Your posted Code does not reveal, if you registered your functions to the BackgroundWorker Events.
Creating a new BackgrounWorker isn't enough.
Here is an example:
public Class Main
{
public Main( ... )
{
BackgroundWorker worker = new BackgroundWorker()
worker.WorkerReportsProgress = true;
// Register to BackgroundWorker-Events
worker.DoWork += threadWorker_DoWork;
worker.ProgressChanged += threadWorker_ProgressChanged;
}
}
in addition you should tell your ProgressBar to rerender.
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Increment(1);
statusBar.Invalidate(true);
}
at least you might want to use the value you have set calling ReportProgress(i * 10).
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Value = e.ProgressPercentage;
statusBar.Invalidate(true);
}

Why is the backgroundworker not pausing when I click the button?

In Form1 I removed/deleted the _busy variable. In Form1 top I did:
BackgroundWebCrawling bgwc;
Then in the button4 pause click event I did:
private void button4_Click(object sender, EventArgs e)
{
bgwc.PauseWorker();
label6.Text = "Process Paused";
button5.Enabled = true;
button4.Enabled = false;
}
In the button5 click event button I did:
private void button5_Click(object sender, EventArgs e)
{
bgwc.ContinueWorker();
label6.Text = "Process Resumed";
button4.Enabled = true;
button5.Enabled = false;
}
And the cancel button click event:
private void button3_Click(object sender, EventArgs e)
{
bgwc.CancelWorker();
cancel = true;
}
Then I'm checking in Form1 completed event if cancel is true or not:
if (cancel == true)
{
label6.Text = "Process Cancelled";
}
else
{
label6.Text = "Process Completed";
}
And this is how the BackgroundWebCrawling class look like now:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HtmlAgilityPack;
using System.Net;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
namespace GatherLinks
{
class BackgroundWebCrawling
{
public string f;
int counter = 0;
List<string> WebSitesToCrawl;
int MaxSimultaneousThreads;
public BackgroundWorker mainBackGroundWorker;
BackgroundWorker secondryBackGroundWorker;
WebcrawlerConfiguration webcrawlerCFG;
List<WebCrawler> webcrawlers;
int maxlevels;
public event EventHandler<BackgroundWebCrawlingProgressEventHandler> ProgressEvent;
ManualResetEvent _busy = new ManualResetEvent(true);
public BackgroundWebCrawling()
{
webcrawlers = new List<WebCrawler>();
mainBackGroundWorker = new BackgroundWorker();
mainBackGroundWorker.WorkerSupportsCancellation = true;
mainBackGroundWorker.DoWork += mainBackGroundWorker_DoWork;
}
private void mainBackGroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < WebSitesToCrawl.Count; i++)
{
_busy.WaitOne();
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
while (counter >= MaxSimultaneousThreads)
{
Thread.Sleep(10);
}
WebCrawler wc = new WebCrawler(webcrawlerCFG);
webcrawlers.Add(wc);
counter++;
secondryBackGroundWorker = new BackgroundWorker();
secondryBackGroundWorker.DoWork += secondryBackGroundWorker_DoWork;
object[] args = new object[] { wc, WebSitesToCrawl[i] };
secondryBackGroundWorker.RunWorkerAsync(args);
}
while (counter > 0)
{
Thread.Sleep(10);
}
}
private void secondryBackGroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] args = (object[])e.Argument;
WebCrawler wc = (WebCrawler)args[0];
string mainUrl = (string)args[1];
wc.ProgressEvent += new EventHandler<WebCrawler.WebCrawlerProgressEventHandler>(x_ProgressEvent);
wc.webCrawler(mainUrl, maxlevels);
counter--;
}
public void Start(List<string> sitestocrawl, int threadsNumber, int maxlevels, WebcrawlerConfiguration wccfg)
{
this.maxlevels = maxlevels;
webcrawlerCFG = wccfg;
WebSitesToCrawl = sitestocrawl;
MaxSimultaneousThreads = threadsNumber;
mainBackGroundWorker.RunWorkerAsync();
}
private void x_ProgressEvent(object sender, WebCrawler.WebCrawlerProgressEventHandler e)
{
// OK .. so now you get the data here in e
// and here you should call the event to form1
Object[] temp_arr = new Object[8];
temp_arr[0] = e.csFiles;
temp_arr[1] = e.mainUrl;
temp_arr[2] = e.levels;
temp_arr[3] = e.currentCrawlingSite;
temp_arr[4] = e.sitesToCrawl;
temp_arr[5] = e.done;
temp_arr[6] = e.failedUrls;
temp_arr[7] = e.failed;
OnProgressEvent(temp_arr); /// Send the data + additional data from this class to Form1..
///
/*
* temp_arr[0] = csFiles;
temp_arr[1] = mainUrl;
temp_arr[2] = levels;
temp_arr[3] = currentCrawlingSite;
temp_arr[4] = sitesToCrawl;*/
}
private void GetLists(List<string> allWebSites)
{
}
public class BackgroundWebCrawlingProgressEventHandler : EventArgs
{
public List<string> csFiles { get; set; }
public string mainUrl { get; set; }
public int levels { get; set; }
public List<string> currentCrawlingSite { get; set; }
public List<string> sitesToCrawl { get; set; }
public bool done { get; set; }
public int failedUrls { get; set; }
public bool failed { get; set; }
}
protected void OnProgressEvent(Object[] some_params) // Probably you need to some vars here to...
{
// some_params to put in evenetArgs..
if (ProgressEvent != null)
ProgressEvent(this,
new BackgroundWebCrawlingProgressEventHandler()
{
csFiles = (List<string>)some_params[0],
mainUrl = (string)some_params[1],
levels = (int)some_params[2],
currentCrawlingSite = (List<string>)some_params[3],
sitesToCrawl = (List<string>)some_params[4],
done = (bool)some_params[5],
failedUrls = (int)some_params[6],
failed = (bool)some_params[7]
});
}
public void PauseWorker()
{
if (mainBackGroundWorker.IsBusy)
{
_busy.Reset();
}
}
public void ContinueWorker()
{
_busy.Set();
}
public void CancelWorker()
{
ContinueWorker();
mainBackGroundWorker.CancelAsync();
}
}
}
So I added the methods the pause the continue the cancel. In the dowork event, I changed all the things and added things.
But when I click the buttons there is no effect. Not pausing, not continue and not cancel. Nothing.
You never check the _busy status in mainBackGroundWorker_DoWork method;
for (int i = 0; i < WebSitesToCrawl.Count; i++)
{
_busy.WaitOne();
//...
}
also you should have your ManualResetEvent _busy in class with BackgroundWorker
ManualResetEvent _busy = new ManualResetEvent(true);
public BackgroundWorker mainBackGroundWorker;
public void PauseWorker()
{
if(mainBackGroundWorker.IsBusy)
{
_busy.Reset();
}
}
public void ContinueWorker()
{
_busy.Set();
}
and in Form1:
private void button4_Click(object sender, EventArgs e)
{
bgwc.PauseWorker();
//...
}
private void button5_Click(object sender, EventArgs e)
{
bgwc.ContinueWorker();
//...
}
to cancel the BackgroundWorker you can use CancellationPending property and CancelAsync method. Note: you should first unpause the worker.
public void CancelWorker()
{
ContinueWorker();
mainBackGroundWorker.CancelAsync();
}
private void mainBackGroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < WebSitesToCrawl.Count; i++)
{
_busy.WaitOne();
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
//...
}
}
If this doesn't help you, then you have problems with mainBackGroundWorker code and secondryBackGroundWorker.
This code only pauses mainBackGroundWorker, but not secondryBackGroundWorkers. The same with cancelation. If main worker is canceled? it will wait for all the secondary workers to finish their jobs. Also if you pause main worker? you can still have new results arriving from secondary workers.
You do not handle errors. If you have an exception in second worker, than you do not get any notification about that and also your main worker will never stop, because counter will never be 0.
There can be another problems, witch cause this behaviour.

Converting Console App to Form with Progress Bar?

I've created a Console App which takes about 5 minutes to run. The main method calls around 10 methods that do the work.
How can I change the Console App to be a Windows Forms application that updates a progress bar/displays the latest progress through these 10 methods?
Many thanks!
You can use BackgroundWorker component in Winforms. Just copy-paste this code. I have used a Label instead of ProgressBar. The label is updated with the percentage completion as the task progresses in the Background.
The time - consuming method / task has to be called in the Do_Work handler. Run the sample below.
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();
}
/// Do the time consuming work here
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;
}
}
}

C# multi-threading

I have a problem with C# multi-threading.
Form contents are two buttons and two lables.
If I press on the first button, going looping from 1..to 60000, to update label1. ( It works)
If I press on the second button, going looping from 1..to 6000 to update label2,(and my form is lagged). (is not responding)
Please help!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(countNumbers));
thread.IsBackground = true;
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(countNumbers2));
thread2.Start();
}
public void countNumbers()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
label2.Text = "" + i.ToString();
}
);
}
}
catch (Exception e)
{
}
}
public void countNumbers2()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
label4.Text = "" + i.ToString();
}
);
}
}
catch (Exception e)
{
}
}
private void label3_Click(object sender, EventArgs e)
{
}
}
}
Try using a Forms.Timer in the form and poll a value at regular intervals to update the label in a controlled way. Updating the UI the way you do puts way to much load on the system.
A System.Windows.Forms.Timer runs on the GUI thread.
Just make sure to guard the shared resource in some way, this example uses a volatile member to handle thread synchronization.
You do not need the extra Thread.Sleep(10), it is just there to simulate some load.
private volatile int _counter;
private readonly Timer _timer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
_timer.Tick += TimerTick;
_timer.Interval = 20; // ~50 Hz/fps
_timer.Start();
}
void TimerTick(object sender, EventArgs e)
{
_label.Text = _counter.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CountNumbers) {IsBackground = true};
thread.Start();
}
public void CountNumbers()
{
for (int i = 0; i < 60000; i++)
{
_counter++;
Thread.Sleep(10); // <-- Simulated work load
}
}
Of course, you can easily expand this example to fit your example with two different counters, calculated on separate threads but still using only one Timer to update the entire UI.
You end up with lagging because Invoke (switching to another thread) is very expensive operation and you are calling it too frequently
Try giving this.Refresh() or Application.DoEvents() in your loop
Try to use lock statement
lock (this)
{
label2.Text = "" + i.ToString();
}
you shoud change your code to
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication23
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(countNumbers));
thread.IsBackground = true;
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(countNumbers2));
thread2.Start();
}
public void countNumbers()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
lock (this)
{
label2.Text = "" + i.ToString();
}
}
);
}
}
catch (Exception e)
{
}
}
public void countNumbers2()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
lock (this)
{
label4.Text = "" + i.ToString();
}
}
);
}
}
catch (Exception e)
{
}
}
private void label3_Click(object sender, EventArgs e)
{
}
}
}
Put some synchronization mechanism there
You can use
1.lock(this);
2.monitor.enter(obj); and monitor.exit(obj);
lock (this){
label2.Text = "" + i.ToString();
}
monitor.enter(obj);
label2.Text = "" + i.ToString();
monitor.exit(obj);

Categories

Resources