In almost all tutorials of BackgroundWorker the reportProgress event is handled like this (this example is from MSDN http://msdn.microsoft.com/en-us/library/cc221403(VS.95).aspx)
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.
// _results.Load() downloads XML and save the data to database
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
}
My function downloads XML and save it to database after parsing. I called this function below "// Perform a time consuming operation and report progress." But won't my function run 10 times?
Later i modified Load() adding to variables CountTotal (total number of results) and CountLoaded (number of results saved, it changes as the function progress).
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.
_results.Load() downloads XML and save the data to database
worker.ReportProgress((_results.CountLoaded * 10)); //i did something like this
}
}
}
The problem is that worker.ReportProgress executes after the completion of _results.Load(). How to solve this problem? Are the given examples on internet really bad because they are suggesting to call the function in a loop, or I got them wrong?
Yes - that will execute Load 10 times. The intent in that example is to illustrate usage when you can estimate the overall workload, or report meaningful progress. They are just trying to simulate "some work" with progress indication.
If you can't do that, then just run it async (via BackgroundWorker) but show a scrolling infinite marquee or similar. Don't do it 10 times ;p Alternatively, run Load, then report progress when you process the data. Assuming that takes some time. If all the time is in Load, then...
Related
This question already has answers here:
WinForm Application UI Hangs during Long-Running Operation
(3 answers)
Closed 5 years ago.
My program is suppose to perform tests on 8 electronic products of the same model simultaneously. The previous programmer has implemented some form of multi-threading in the program to accomplish this. However, when 5 slots or more are being tested, the UI becomes unresponsive and the results being written to a text file may get corrupted.
Below I will insert a pseudo-code on what's going on in the program.
private void button1_Click(object sender, EventArgs e)
{
//create_thread_1 <= mainFunction 1
//start thread 1
}
private void button2_Click(object sender, EventArgs e)
{
//create_thread_2 <= mainFunction 2
//start thread 2
}
private void button3_Click(object sender, EventArgs e)
{
//create_thread_3 <= mainFunction 3
//start thread 3
}
private void mainFunction1
{
//perform test A
//write test A result to textFile1 //calls writeToTextFile1
//perform test B
//write test B result to textFile1 //calls writeToTextFile1
//continues on and finishes all tests
//aborts thread1
//end
}
private void mainFunction2
{
//perform test A
//write test A result to textFile2 //calls writeToTextFile2
//perform test B
//write test B result to textFile2 //calls writeToTextFile2
//continues on and finishes all tests
//aborts thread2
//end
}
private void mainFunction3
{
//perform test A
//write test A result to textFile3 //calls writeToTextFile3
//perform test B
//write test B result to textFile3 //calls writeToTextFile3
//continues on and finishes all tests
//aborts thread3
//end
}
private void writeToTextFile1
{
//creates and saves results into textFile1
}
private void writeToTextFile2
{
//creates and saves results into textFile2
}
private void writeToTextFile3
{
//creates and saves results into textFile3
}
My theory is that only a single thread can open and write data into a text file at a single time, so when another thread have to write data, that thread has to wait and causes the UI to become unresponsive. Am I right here? If I'm not, any advice is greatly appreciated.
One of the solutions that I have read online is to declare the WriteToTextFile function as a new Thread so that other main threads can wait for each other without slowing down the UI. Is this the correct approach?
EDIT: added the important parts of the coding for better understanding..This code runs for one slot only but the other 9 slots basically uses the same code here
private void button1_Click(object sender, EventArgs e)
{
if (this.button1.Text == "START")
{
this.txtSerial1.ReadOnly = false;
this.txtSerial1.Select();
MessageBox.Show("SLOT 1: Scan the serial number and press ENTER", "3458A
Heat Rack", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
else if (System.Windows.Forms.DialogResult.OK == MessageBox.Show("SLOT 1: Are
you sure about stopping?", "3458A Heat Rack",
MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation))
{
this.call_main1.Abort();
this.sentry1.Close();
this.sentry1.Dispose();
MessageBox.Show("SLOT 1: Unit can be safely removed now", "3458A Heat
Rack", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
this.txtSerial1.Clear();
this.txtStart1.Clear();
this.txtStatus1.Clear();
this.info1.Clear();
this.button1.Text = "START";
this.button1.BackColor = this.startColour;
this.txtStatus1.BackColor = Control.DefaultBackColor;
}
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
int num;
int test_num = default(int);
double resultrelay = default(double);
if (e.KeyChar == '\r')
{
if (this.txtSerial1.Text.Length == 0)
{
this.txtSerial1.ReadOnly = true;
}
else if (this.txtSerial1.Text.Length >= 10)
{
try
{
this.sentry1 = new DirectIO(string.Concat("GPIB",
this.busNumber_Slot1, "::22::INSTR"));
this.terminal1 = new DirectIO(string.Concat("GPIB0::14::INSTR"));
num = 1;
}
catch (Exception exception)
{
num = 0;
}
if (num != 1)
{
MessageBox.Show("SLOT 1: DUT Not Present !!", "3458A Heat Rack",
MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
this.txtSerial1.Clear();
this.txtSerial1.Select();
this.txtSerial1.ReadOnly = true;
}
else
{
this.button1.Select();
this.button1.Text = "RUNNING";
this.button1.BackColor = this.runningColour;
this.txtSerial1.Text = this.txtSerial1.Text.ToUpper();
this.txtStart1.Text = DateTime.Now.ToString();
this.txtSerial1.ReadOnly = true;
string txtBox1_serial = this.txtSerial1.Text;
this.call_main1 = new Thread(() => this.main_Program_slot1(sentry1,
terminal1, txtBox1_serial, 1, test_num,
resultrelay));
this.call_main1.Start();
}
}
else
{
MessageBox.Show("SLOT 1: Unit Serial Number Is Incorrect!!", "3458A
Heat Rack", MessageBoxButtons.OK,
MessageBoxIcon.Asterisk);
this.txtSerial1.Clear();
this.txtSerial1.Select();
}
}
}
public void slot1(string test) //function to update GUI
{
if (!base.InvokeRequired)
{
this.info1.Text = test;
}
else
{
Form1.test1 updateTestType = new Form1.test1(this.slot1);
object[] objArray = new object[] { test };
base.Invoke(updateTestType, objArray);
}
}
private void write_TestResultDetails1(string serialnumber, double resultLatest1)
{
//update results into textfile
}
private void main_Program_slot1(DirectIO sentry1, DirectIO terminal1, string sernum, int slot_Number, int test_num, double resultrelay)
{
for (int i = 1; i <= loop_Count; i++)
{
slot1("TEST A");
//performs testA
//calls write_TestResultDetails1
slot1("TEST B");
//performs testB
//calls write_TestResultDetails1
}
}
Hope this coding can help you guys to understand my problem better..
PS: seems like changing to using BackGroundWorker instead of making my own threads will be a better choice for this kind of program.
Windows forms programming has a few gotchas and keeping a UI responsive is tough. To help you debug this issue, I recommend you name your thread in form load so you can easily find it in the debugger (double click your form in the designer to get form load then call System.Threading.Thread.CurrentThread.Name = "My UI Thread". Launch your application and then when the UI hangs, break in the debugger. You can then observe the stacktrace of the UI Thread to find out where it is working (and where you need to use a thread to keep the UI responsive.
My hunch is that you have either used a synchronisation primitive incorrectly waiting for an answer on a thread, or you have accidentally launched some work without a thread which is hanging the UI.
There is a control called a BackgroundWorker which can be used to do work on a thread easily and then report the progress back with an event safely. This cuts down on the synchronisation work you need to do which might be helpful.
Totally agree with the previous comments btw, please post your actual code, just redact the method names etc. as the most likely issue is that your psuedo code and your actual code don't match.
I/O intensive tasks are perfect for asynchronous actions, i.e. writeToTextFile1(), writeToTextFile2(), and writeToTextFile3() can all be executed on different threads.
in your solution, the error might be caused from the fact that you wrapped two/three I/O method calls inside one thread.
I suggest you adopt the following pattern.
take writeToTextFile1() for example, I would use async/await pattern to define this method:
private async Task writeToTextFile1Async(string resultValue)
{
await Task.Run(() => {
//create and saves results into textFile1
});
}
rewrite mainFunction1() as follows:
private async Task mainFunction1Async()
{
string resultA = "***";
await writeToTextFile1Async(resultA);
string resultB = "***";
await writeToTextFile1Async(resultB);
//perform other things
}
Call this function inside Button1 click event handler:
private async void button1_Click(object sender, EventArgs e)
{
await mainFunction1Async();
}
I try to perform an easy task in an other backgroundthread, so the UI doesn't get blocked, but it still gets blocked. Did I forget anything?
public partial class backgroundWorkerForm : Form
{
public backgroundWorkerForm()
{
InitializeComponent();
}
private void doWorkButton_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker.RunWorkerAsync();
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker worker = sender as BackgroundWorker;
if (textBoxOutput.InvokeRequired)
{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
for (int i = 0; i < 10000; i++)
{
textBoxOutput.AppendText(i + Environment.NewLine);
}
}));
}
}
}
While the textBox gets filled, the UI is blocked:
Your app wants to repeatedly send updates from the background thread to the UI. There is a built-in mechanism for this: the ProgressChanged event for the background worker. A ReportProgress call is triggered in the background, but executes on the UI thread.
I do change one thing, however. Performance can degrade with too many cross-thread calls. So instead of sending an update every iteration, I instead will batch them into 100.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
const int maxIterations = 10000;
var progressLimit = 100;
var staging = new List<int>();
for (int i = 0; i < maxIterations; i++)
{
staging.Add(i);
if (staging.Count % progressLimit == 0)
{
// Only send a COPY of the staging list because we
// may continue to modify staging inside this loop.
// There are many ways to do this. Below is just one way.
backgroundWorker1.ReportProgress(staging.Count, staging.ToArray());
staging.Clear();
}
}
// Flush last bit in staging.
if (staging.Count > 0)
{
// We are done with staging here so we can pass it as is.
backgroundWorker1.ReportProgress(staging.Count, staging);
}
}
// The ProgressChanged event is triggered in the background thread
// but actually executes in the UI thread.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 0) return;
// We don't care if an array or a list was passed.
var updatedIndices = e.UserState as IEnumerable<int>;
var sb = new StringBuilder();
foreach (var index in updatedIndices)
{
sb.Append(index.ToString() + Environment.NewLine);
}
textBoxOutput.Text += sb.ToString();
}
EDIT:
This requires you set the background worker's WorkerReportsProgress property to true.
It's not important that you pass a count with the ReportProgress call. I do so just to have something and to quickly check if I can return.
One really should keep in mind about how many events are being invoked and queued up. Your original app had 10,000 cross thread invocations and 10,000 changed text events for textBoxOutput. My example uses 100 cross thread calls since I use a page size of 100. I could still have generated 10,000 changed text events for the textbox, but instead use a StringBuilder object to hold a full page of changes and then update the textbox once for that page. That way the textbox only has 100 update events.
EDIT 2
Whether or not your app needs paging is not the main deal. The biggest take away should be that the background worker really should use ReportProgress when trying to communicate info back to the UI. See this MSDN Link. Of particular note is this:
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.
Your invocation code should be outside the loop. Everything in the invoked codeblock, will be executed on the UI thread, thus blocking it.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 10000; i++)
{
// do long-running task
//if (textBoxOutput.InvokeRequired)
//{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
textBoxOutput.AppendText(i + Environment.NewLine);
}));
//}
}
}
an easier way would be to do completely create your output text, and then paste the full output into the TextBox, then you only need one invocation
protected delegate void SetTextDelegate(TextBox tb, string Text);
protected void SetText(TextBox tb, string Text)
{
if (tb.InvokeRequired) {
tb.Invoke(new SetTextDelegate(SetText), tb, Text);
return;
}
tb.Text = Text;
}
and then inside your dowork
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
StringBuilder sb = new StringBuilder();
//BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 10000; i++)
{
sb.AppendLine(i.ToString());
}
SetText(textBoxOutput, sb.ToString());
}
I had a SQL Stored Procedure that get a mbd file and import it using itself in less than 3 second but because of the security of the shared server I can't do it anymore but instead I can use vb.net to import necessary temp tables to Sql server and continue the process Unfortunately it gets so long to complete the process (about 3 minutes for a 3MegaByte mdb file), and I need to show client the process so the client can wait patiently and know how far the process has gone.
I have seen many things related to this but they are all showing an image loading instead of exact progress bar,My question is: Is there any possible way to show the progress bar while based on how the process is doing?
PS: I can place the show progress percent in my for loop in vb.net.
EDIT: To be specific I just need to know how can I show the client the progress and just update the progress bar in html or maybe change the progress bar width style?
Thanks
You can use a BackGroundWorker:
When using backgroundWorker, it is always convenient for me to use #Keith template
BackgroundWorker bw = new BackgroundWorker { WorkerReportsProgress = true };
bw.DoWork += (sender, e) =>
{
//what happens here must not touch the form
//as it's in a different thread
//Here you should call the function that does the heavy, slow work.
//pass the BackgroundWorker instance (bw) as an argument
};
bw.ProgressChanged += ( sender, e ) =>
{
//update progress bars here
};
bw.RunWorkerCompleted += (sender, e) =>
{
//now you're back in the UI thread you can update the form
//remember to dispose of bw now
};
worker.RunWorkerAsync();
In you function update about the progress by using something like:
void YourFunction(BackgroundWorker bw)
{
for (int i = 0; i < length; i++)
{
//do your work
int percent = (i / length) * 100;
bw.ReportProgress(percent);
}
}
I'm attempting a very DB intensive task in a project. Here is a walk-through:
We need to search our DB of workers, we called Locums, and find one for a specific job. This procedure starts when we decide to process x number of jobs. So, on the click of a button, we process using the ProcessJobBatch() method. However, this method only process against a very limited number of Locums. So it takes less then 10 seconds to fill up a scheduler control. Now, once the limited number of Locums are served, we need to run a background task to check the rest of the Locums. There are around 1250 of them!
So, once ProcessJobBatch() finishes, a BackgroundWorker, BackgroundWorkerMoreLocums, goes off. Now, this worker basically does a simple loop: For each job, go through the whole 1250 employees. This takes way too long. I need to plan this out using an alternate strategy that I can't of ATM or I need to show a secondary progress bar for the inner for-each loop.
More Explanation: We import a batch of Jobs (10 to 70) numerous times on daily bases. Once a batch is imported, the application instructs the logged-in user to "Preference Find" those newly created jobs. The user already has a list of his favorite locums (1 to 20). He wants to distribute the jobs among his favorites first. That is done through ProcessJobBatch(). But, there are two scenarios that prevent the flow there and then:
What if certain jobs didn't fall to
any favorite locum?
What if there is a locum in the whole
DB who can do almost all the jobs but
since he isn't favorite?
So, I end up with a scenario of matching a job with each Locum.
Question:
Can second BackgroundWorker run within a BackgroundWorker's DoWork?
Am I doing the second scan wrong?
Environment: Windows 7 Pro 64-bit, Visual Studio 2010, C#, .NET 4.0, and Windows Forms
private void ButtonPreferenceFind_Click(object sender, EventArgs e) {
if (LookUpBatches.EditValue != null) {
JobBatch JobBatchSelected = DbContext.JobBatches.FirstOrDefault(job_batch=> job_batch.OID == LookUpBatches.EditValue.ToString());
if (JobBatchSelected != null && JobBatchSelected.Jobs.Count(condition => condition.JobStatusID == 1) > 0) {
if (XtraMessageBox.Show(String.Format("Are you sure to process {0} job(s)?", JobBatchSelected.Jobs.Count(condition => condition.JobStatusID == 1)), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
ProcessJobBatch(JobBatchSelected);
IEnumerable<Job> SpecificJobs = from req_jobs in JobBatchSelected.Jobs
where req_jobs.JobStatusID == 1
select req_jobs;
ProgressBarControlPreferenceFinder.EditValue = 0;
ProgressBarControlPreferenceFinder.Properties.Minimum = 0;
ProgressBarControlPreferenceFinder.Properties.Maximum = SpecificJobs.Count() - 1;
BackgroundWorkerMoreLocums.RunWorkerAsync(SpecificJobs);
} else {
LookUpBatches.Focus();
}
} else {
XtraMessageBox.Show("Unable to retrieve the selected batch or the batch has no processable jobs.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
LookUpBatches.Focus();
}
} else {
XtraMessageBox.Show("Select a batch first.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
LookUpBatches.Focus();
}
}
#region Background Searching
private void BackgroundWorkerMoreLocums_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
try {
e.Result = GetTableData(e.Argument);
}
catch (Exception ex) {
XtraMessageBox.Show("Background Error: " + ex.Message, "Excite Engine 2", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Result = ex;
}
}
private void BackgroundWorkerMoreLocums_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
// only display progress, do not assign it to grid
ProgressBarControlPreferenceFinder.Increment(e.ProgressPercentage);
}
private void BackgroundWorkerMoreLocums_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
if (e.Result is DataTable) {
//dataGridView1.DataSource = e.Result as DataTable;
}
else if (e.Result is Exception) {
}
}
private DataTable GetTableData(Object JobList) {
DataTable ResultDataTable = new DataTable();
ResultDataTable.Columns.Add();
IEnumerable<Job> JobBatchSelected = (IEnumerable<Job>)JobList;
IEnumerable<Locum> LeftOverLocums = from lefties in DbContext.Locums
//where SchedulerMatrixStorage.Resources.Items.Select(res => (long)res.Id).ToList().Contains(lefties.OID) == false
select lefties;
int NumOfJobsProcessed = 0;
List<KeyValuePair<long, TemporaryPreferenceFindLocum>> AlreadyPrefferedLocums = new List<KeyValuePair<long, TemporaryPreferenceFindLocum>>();
foreach (Job oneJob in JobBatchSelected) {
foreach (Locum oneLocum in LeftOverLocums) {
if (DbContext.Availabilities.Any(check => check.LocumID == oneLocum.OID && check.AvailableDate == oneJob.JobDate && check.AvailabilityStatusID == 1)) {
//This Locum can do this job
//Now check if he/she has been just alloted
if (AlreadyPrefferedLocums.Any(search => search.Key == oneLocum.OID && search.Value.JobDate == oneJob.JobDate) == false) {
//No? Cool!
//Add to the list to prevent double allocation
AlreadyPrefferedLocums.Add(new KeyValuePair<long, TemporaryPreferenceFindLocum>(oneJob.OID, new TemporaryPreferenceFindLocum(oneJob.JobDate, oneJob.OID, oneLocum.OID, oneLocum.FirstName + " " + oneLocum.LastName)));
}
else {
continue;
}
}
else {
//Not marked as Avaliable on the required job date...
continue;
}
}
NumOfJobsProcessed++;
BackgroundWorkerMoreLocums.ReportProgress((int)(NumOfJobsProcessed * 100F / (JobBatchSelected.Count() - 1)));
}
return ResultDataTable;
}
#endregion
A BackgroundWorker can be started from within the DoWork handler of another BackgroundWorker, but you need to be aware of the consequences of using such a scheme. When you start a background worker from your main UI thread the DoWork handler is executed on a thread pool thread while the ProgressChanged and RunWorkerCompleted are executed back on the main UI thread making it safe for you to interact with windows forms controls.
This scenario is guaranteed when you start the worker from the main UI thread because it picks up the SynchronizationContext available on that thread and which is initialized by the windows forms infra-structure.
However, when you start a background worker from the DoWork handler of another worker, you'll be starting it from a thread pool thread that lacks the synchronization context causing the ProgressChanged and RunWorkerCompleted handlers to also be executed on thread pool threads and not in your main UI thread making it unsafe for you to interact with windows forms controls from within those handlers.
It is quite common to have one background thread spawn new background threads. I don't think it is a problem if you scan the list on a background thread and process each list item on another thread.
In such cases there is no background worker within another. There is just a background worker starting other threads.
Things you should consider -
Be aware of what you do in the completed event handlers in case you handle that event.
Consider the performance implications of running so many threads for small tasks. You should consider using PLINQ or parallel tasks so that .Net can handle the partitioning of input and merging of results giving you optimum performance.
I have a form (Developed in C# using VS2010) with a Progress Bar.
It's kind of stopwatch form where I fill the progress bar in say 10secs.... As Time elapses, Progress bar fills accordingly.... Means after 5secs, Progress Bar will be filled 50% and so on....
I used a for loop to perform this operation:-
for(int count=0;count<200;count++)
{
progbar.performstep();
progbar.update();
update();
Thread.Sleep(50);
}
I have used Thread.Sleep of 50msec so that progress bar updates smoothly.
For 1sec, it increments in steps.
Problem is if I do anyother operation like Moving this form or even clicking on another icon on desktops makes this form "Not Responding".... But it perfoms the operation and at the end of 10 secs, It regains it's state and shows the pop up indicating 10secs are elapsed with Progress Bar Full.
Thanks for help and Sorry for using such complicated language.
Regards,
Swanand
Update: I solved this problem with the help of Answers below.... One common mistake I got to know is forgetting "Applications.DoEvents()" along with "update()".... If you enter this line, there are less chances of getting "hanged"!
You're performing a long-running operation on the UI thread, which means that the UI "message loop" (responsible for handling events such as user input and updating the screen) doesn't get a chance to run.
You should perform the long-running operation on a different thread (whether one you create yourself or a background thread) and either use BackgroundWorker to easily update your progress bar, or use Control.Invoke/BeginInvoke to marshall a delegate call back to the UI thread when you need to update the UI. (You mustn't update controls from the wrong thread.)
If your only UI interaction is filling in a progress bar, I suggest using BackgroundWorker.
If you're not really doing "real" work, just waiting for time to pass, you could use a System.Windows.Forms.Timer instead of all of this, however. That will "tick" on the UI thread, but won't block the UI thread between ticks. You should only use this if you don't have a lot of work to do though - if it really is just updating a progress bar, not (say) processing a file etc. Note that you shouldn't rely on the timer firing exactly "on time" - you should probably set the position of the progress bar based on the observed time, rather than the observed number of ticks.
You are blocking the UI thread, which means it isn't processing events such as "paint". To do this properly, you should be using something like BackgroundWorker, and just updating the UI from the progress event.
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
}
class MyForm : Form
{
Button btn;
BackgroundWorker worker;
ProgressBar bar;
public MyForm()
{
Controls.Add(btn = new Button { Text = "Click me" });
btn.Click += new EventHandler(btn_Click);
Controls.Add(bar = new ProgressBar { Dock = DockStyle.Bottom, Visible = false, Minimum = 0, Maximum = 100 });
worker = new BackgroundWorker { WorkerReportsProgress = true };
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bar.Visible = false;
if (e.Error != null)
{
Text = e.Error.Message;
}
else if (e.Cancelled)
{
Text = "cancelled";
}
else
{
Text = e.Result == null ? "complete" : e.Result.ToString();
}
btn.Enabled = true;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int count = 0; count < 100; count++)
{
worker.ReportProgress(count);
Thread.Sleep(50);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
bar.Value = e.ProgressPercentage;
}
void btn_Click(object sender, EventArgs e)
{
bar.Value = 0;
bar.Visible = true;
btn.Enabled = false;
worker.RunWorkerAsync();
}
}
You are blocking the Main UI thread. You can use a background worker to do this. You can find more details in MSDN
If you want to run your code you should put this code in a function and call this function with one thread.
public static void fun1()
{
for (int i = 0; i <= 10; i++)
{
Console.Write("This is function1");
Console.Write("\n");
}
}
Thread firstthread = new Thread(new ThreadStart(fun1));
firstthread.Start();
firstthread.suspend();//whenever you want your current control to stop.
b'caz Thread.sleep(100) will stop the whole context not that particular you want..
Answer suggested by Marc will help. Lon running operations can make your application crash or not responsive. I have a blog post related to the usage of the background worker class.
http://midnightprogrammer.net/post/Using-Background-Worker-in-C.aspx