Basically i would like to update ProgressBar UI object on the FormMain (WindowsForm). I am using .NET 4.0
Here are the code in the Form1.Designer.cs
namespace ProgressBarApp
{
public partial class Form1 : Form
{
private System.Windows.Forms.ProgressBar curProgressBar;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
CustomProcess theProcess = new CustomProcess();
theProcess.Process();
}
}
}
Here is the definition of CustomProcess.cs
namespace ProgressBarApp
{
class CustomProcess
{
public void Process()
{
for (int i = 0; i < 10; i++)
{
Task ProcessATask = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // simulating a process
}
);
Task UpdateProgressBar = ProcessATask.ContinueWith((antecedent) =>
{
// how do i update the progress bar object at UI here ?
}
);
}
}
}
}
You can use SynchronizationContext to do this. To use it for a Task, you need to create a TaskScheduler, which you can do by calling TaskScheduler.FromCurrentSynchronizationContext:
Task UpdateProgressBar = ProcessATask.ContinueWith(antecedent =>
{
// you can update the progress bar object here
}, TaskScheduler.FromCurrentSynchronizationContext());
This will work only if you call Process() directly from the UI thread.
How about using System.Reactive.Linq:
[UPDATE]
using System.Reactive.Linq;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
//private System.Windows.Forms.ProgressBar curProgressBar;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
CustomProcess theProcess = new CustomProcess();
var x = Observable.FromEventPattern(theProcess, "TaskCompleted");
curProgressBar.Maximum = 4;
x.Subscribe((a) =>
{
curProgressBar.Value = ((CustomProcess)a.Sender).Counter;
});
theProcess.Process();
}
}
class CustomProcess
{
public int Counter { get; set; }
public event EventHandler TaskCompleted = OnTaskCompleted;
private static void OnTaskCompleted(object sender, EventArgs e)
{
((CustomProcess)sender).Counter++;
}
public void Process()
{
for (int i = 0; i <= 3; i++)
{
Task ProcessATask = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // simulating a process
}
);
var awaiter = ProcessATask.GetAwaiter();
awaiter.OnCompleted(() =>
{
TaskCompleted(this, null);
});
}
}
}
}
Related
My question is similar to this one, I have pretty much the same code setup except I'm using BackgroundWorker instead of WorkflowRuntime. (And the answer doesn't appear to work for me)
In the past I have used Application.Current.Shutdown(); in the closing event of MainWindow, however I was hoping that by properly disposing of this window which I've made a static resource I could perhaps not need that.
The problem is that if I exit via closing MainWindow after all the background tasks terminate an empty BackgroundDialog remains open.
public partial class BackgroundDialog : Window
{
private static BackgroundDialog _Dialog = new BackgroundDialog();
private static UIElementCollection TasksView { get { return _Dialog.BackgroundList.Children; } }
public static void Add(BackgroundItem item)
{
if (TasksView.Count == 0)
{
_Dialog.Show();
}
TasksView.Add(item);
}
public static void Remove(BackgroundItem item)
{
TasksView.Remove(item);
if (TasksView.Count == 0)
{
if (_Release)
{
FinishRelease();
}
else
{
_Dialog.Hide();
}
}
}
private static bool _Release = false;
private static void FinishRelease()
{
// FinishRelease may be called from a BackgroundWorker thread finishing
// This results in _Dialog.Close() not behaving as expected
// For more details: https://stackoverflow.com/questions/5659930/wpf-window-not-closing
_Dialog.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_Dialog.Close();
_Dialog = null;
}));
}
public static void Release(EventArgs e)
{
_Release = true;
if (TasksView.Count == 0)
{
FinishRelease();
}
else foreach (BackgroundItem Task in TasksView)
{
Task.Abort();
}
}
}
public partial class BackgroundItem : UserControl
{
public delegate void TaskHandler(BackgroundWorker Worker);
public interface IBackgroundTask
{
bool IsIndeterminate { get; }
int MaxProgress { get; }
string Title { get; }
string Description(int progress);
TaskHandler Exec { get; }
}
private BackgroundWorker Worker;
public BackgroundItem(IBackgroundTask task)
{
InitializeComponent();
Title.Text = task.Title;
Description.Text = task.Description(0);
Progress.Value = 0;
Progress.Minimum = 0;
Progress.Maximum = task.MaxProgress;
Progress.IsIndeterminate = task.IsIndeterminate;
BackgroundDialog.Add(this);
Worker = new BackgroundWorker()
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true,
};
Worker.DoWork += (object sender, DoWorkEventArgs e) =>
{
task.Exec?.Invoke(Worker);
};
Worker.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) =>
{
BackgroundDialog.Remove(this);
};
Worker.ProgressChanged += (object sender, ProgressChangedEventArgs e) =>
{
Progress.Value = e.ProgressPercentage;
Description.Text = task.Description(e.ProgressPercentage);
};
Worker.RunWorkerAsync();
Stop.Click += (object sender, RoutedEventArgs e) =>
{
Abort();
};
}
public void Abort()
{
Worker.CancelAsync();
Stop.IsEnabled = false;
StopText.Text = "Stopping";
}
}
public partial class MainWindow : Window
{
private class MyTask : BackgroundItem.IBackgroundTask
{
public bool IsIndeterminate => true;
public int MaxProgress => 100;
public string Title => "I'm Counting";
public BackgroundItem.TaskHandler Exec => (BackgroundWorker Worker) =>
{
for (int i = 0; i < 100; ++i)
{
if (Worker.CancellationPending)
{
break;
}
Worker.ReportProgress(i);
Thread.Sleep(500);
}
};
public string Description(int progress)
{
return progress.ToString();
}
}
public MainWindow()
{
InitializeComponent();
Loaded += (object sender, RoutedEventArgs e) => {
new BackgroundItem(new MyTask());
new BackgroundItem(new MyTask());
new BackgroundItem(new MyTask());
};
}
protected override void OnClosed(System.EventArgs e)
{
base.OnClosed(e);
BackgroundDialog.Release(e);
}
}
Try looking into Application.ShutdownMode. You'll want to set ShutdownMode to be OnMainWindowClose.
I feel silly, must have been the end of the day on Friday....here was the problem
in BackgroundDialog:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
}
Must have been a relic from before I found this solution. However, some cancellation is needed to prevent the user from closing the dialog from the taskbar. So I wrapped the cancel with the statement if (!_Release)
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!!
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);
}
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.
Everybody.
I've two form as follow by
From1 has a button, when click this Form2 will appear.
From2 has a Progressbar, It's counting and update Progressbar from Maximun value until It has finished, Form2 will close.
This below code of Form2
public delegate void ProgressbarHandler(int value);
public partial class Form2 : Form
{
public event WaitCallback CloseThreadEvent;
private Thread t;
public void OnCloseEvent(ThreadState state)
{
if (CloseThreadEvent != null)
CloseThreadEvent(state);
}
public Form2()
{
InitializeComponent();
progressBar1.Minimum = 0;
progressBar1.Maximum = 20000;
}
private void Form2_Load(object sender, EventArgs e)
{
InitThread();
}
private void InitThread()
{
t = new Thread(new ThreadStart(RunThread));
t.Start();
CloseThreadEvent += new WaitCallback(CloseForm);
Thread tt = new Thread(ThreadObserver);
tt.IsBackground = true;
tt.Start();
}
private void RunThread()
{
for (int i = 0; i < progressBar1.Maximum; i++)
{
progressBar1.Invoke(new ProgressbarHandler(UpdateProgressbar), i);
}
}
private void UpdateProgressbar(int value)
{
progressBar1.Value = value + 1;
}
private void ThreadObserver()
{
while (t.IsAlive)
{
OnCloseEvent(t.ThreadState);
}
}
private void CloseForm(Object state)
{
if ((ThreadState)state == ThreadState.Stopped)
this.Close();
}
}
From my code, It has a "Cross-thread operation not valid" error on
this.Close();
Please give suggestion, How to coding follow by my purpose.
Thank you.
You can only access controls from the thread they were created on. A form is also a control.
Have a look at Control.Invoke.
I use a class similar to this to handle these scenarios:
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(new MethodInvoker(action), null);
}
else
{
action.Invoke();
}
}
}
Then you would be able to call this.Invoke(() => Close()); to close your form.
Pretty simple fix.
this.Invoke(new MethodInvoker(delegate { this.Close(); }));
I don't know why you didn't think of it? Isn't it obvious? :P
Thank you, Everybody
It works!!
public partial class Form2 : Form
{
private Thread tstart, trun;
public Form2()
{
InitializeComponent();
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
}
private void Form2_Load(object sender, EventArgs e)
{
tstart = new Thread(InitThread);
tstart.Start();
}
private void InitThread()
{
trun = new Thread(new ThreadStart(RunThread));
trun.Start();
trun.Join();
CloseForm(trun.ThreadState);
}
private void RunThread()
{
for (int i = 0; i < progressBar1.Maximum; i++)
{
Thread.Sleep(10);
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Increment(1); }));
}
}
private void CloseForm(Object state)
{
if ((ThreadState)state != ThreadState.Stopped)
return;
else
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(delegate { this.Close(); }), null);
}
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (tstart.IsAlive)
tstart.Abort();
if (trun.IsAlive)
trun.Abort();
}
}