I have two threads that look like this
pbDB_running = true; // start progress bar
Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));
connectDb.Start();
runProgress.Start();
connectDb.Join(); //wait untill the connection is done
pbDB_running = false; //stop the progress bar
as you probably might have guessed, ConnectToDb is used to make a connection to a database, while runpbDB is making a progress bar run on the interface. The progress bar (pbDB) is a Windows.Forms control created with drag and drop on the design view.
The runProgress thread is running RunpbDB() wich looks like this :
private void RunpbDB()
{
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
When the two threads start I get the following exception inside RunpbDB() :
Cross-thread operation not valid: Control 'pbDB' accessed from a thread other than the thread it was created on.
What can I do to overcome this situation?
Have you thought about using a BackgroundWorker? This might make your life a lot easier. You could set two up, one for your database call and the other for your progress bar. Just listen for the background workers ProgressChanged and RunWorkerCompleted events.
More information on MSDN
Use Control.Invoke method to remedy this problem. The whole your solution will became
private void RunpbDB()
{
while (pbDB_running)
{
Invoke((Action)(()=>{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;}));
}
Invoke((Action)(()=>{pbDB.Value = 0;});
}
You can use something along the lines of pbDB.InvokeRequired and if so, call pbDB.Invoke to perform your action back on the UI thread.
You do not need the check if you know it will always be done on a separate thread than the UI thread.
Here is a link to some code on this and other ways to accomplish this.
You could also use a BackgroundWorker
This is a security imposed by microsoft for its .NET technology. It basically happens when you access a winforms element from a separate thread, i.e. not in the main thread where the GUI winforms is running. The solution is to create a delegate for your RunpbDB method. See the solution here Best Way to Invoke Any Cross-Threaded Code?. in here too: How to update the GUI from another thread in C#?
Just make your life easier and use a BackgroundWorker for this if you don't have access to .NET 4.0. If you can use 4.0+, use the TPL. And if you can use 4.5, you can use the new async/await functionality. There are tons of examples here on Stack Overflow. Here is a link from Stephen Cleary comparing them.
Cross thread operation call when UI thread is involved was discouraged by VS team in 2.0(before that it was possible for security reasons. There are two ways to overcome this issue. Easy way is to set the static property
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
to false which then disables this check globally from your application. But this solution is not advised by any one and not even by me since it again opens the security holes.
Another ways is, in the method first check if UI control needs Invoke,If so then use control's invoke method to invoke the current method again and then return. Code can better clear what I want to say so
private void RunpbDB()
{
if (pbDB.InvokeRequired)
{
pbDB.Invoke(new Action(RunpbDB));
return;
}
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
I'm novice in program with c#. I want to create thread that move label in the main UI without stuck the UI until the movement done
I built something but it didnt work
tell me what is my problem
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(Movelb);
t.IsBackground = true;
t.Start();enter code here
}
private void DOsomeThing()
{
label2.Visible = true;
label2.Location = new Point(0, 205);
for (int i = 0; i < 533; i++)
{
label2.Location = new Point(i, 205);
Thread.Sleep(10);
}
label1.Text="false";
}
private void Movelb()
{
if (this.InvokeRequired)
{
threadDel d = new threadDel(DOsomeThing);
this.BeginInvoke(d);
}
else
DOsomeThing();
}
Do not use threads to paint to forms or modify/update form contents. The recommended paradigm in Windows programming is One Thread Per Form or Window. If you want to create forms that run from separate threads, then you must
create the new thread first
create the Form on the new thread
In this way, the new thread will serve as the new Form's message handler. But even then, you should still do all manipulation of the Form within that thread (and if the form wants to modify contents in another form running on a different thread, then some additional thread-safe communication trickery may be required).
To animate window contents, you should use System.Windows.Forms.Timer instead, which executes on the Form's thread in lock-step with its other messages. You'll need to re-implement your animation as a state machine rather than a for() loop construct, though. That means the variables for Label position will need to be embedded into the Form class, so that updates can be preserved across Timer message invocations.
You need to understand the event model first. In event-driven environments like Windows or Android or Linux etc... the "automatic " tasks such as animations of coordinates or other properties are usually done using Timers that keep re-sending events back to the handler that advances the animation/process. In your particular example - if you need to move label, use Widows.Forms.Timer. It is not appropriate to block UI thread that processes events with lengthy tasks as UI thread will stall and your app will freeze or become jerky. NOW, on the other hand there are many cases when adding extra threads DOES help a lot, when? Not in your case, because you only change the coordinate of the label that is nothing in terms of CPU in comparison to repaint, so your solution with extra thread is LESS efficient and much more complex than using timer. An extra thread is beneficial only when the logical work it performs on animation model is comparable or out-weights the paint work- imagine a game where 200 bugs need to be animated on screen according to many logical rules, in this case bug painting may be done in UI thread, but bug property changes/animations may be done in another thread if those computations are intense.
How Events work?
An OS has an infinite loop inside that gets interrupted by keyboard, mouse and other events but the loop spins indefinitely until you shut down Windows (or Android or XWidnws...). At the end of the loop the OS looks at "raw" mouse/key events and dispatches them into appropriate application queue. It knows it by inspecting every app windows list, who is on top and thus it knows what window/app was under such and such X,Y mouse coordinate. When event gets dispatched to your app your job is to handle it very fast and look for another event in your queue (queues are bound to UI Threads/Windows).
How Timers Work?
A timer is a special kind of event that OS can keep sending to you periodically from its internal "infinite loop". OS keeps track of what apps requested to be notified and how often - when time comes, it adds a WM_TIMER(on MS Windows) into your windows queue. This way you don't block anything, but get a method in your code that gets called every X milliseconds. When you use .NET Timer class - it is just a wrapper around CreateTimer() KillTimer() (I dont recall exact func names) in Windows User APIs. .NET Timer also knows how to swallow the WM_TIMER and call a C# event/delegate for you.
I hope this helps!
Your code does nothing useful. It just starts a new background thread, which, in turn, invokes a delegate, being executed at the same UI thread, which had started... the background thread.
In other words, you can't move the label in worker thread, because moving the label brings to repainting, which can't be done from background thread.
I also had an idea of doing some work in a thread - and while this hard job
was carried out... the main-gui-form should be modified, so the user will
spot a progress.
Did some lookup and went into "delegates", "eventhandlers", and "very advanced pieces of code".
It took me some time to fix, and I came up with this very simple example. Have a look.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ProcessingUI
// You will find a form with "button1": will do some work in a seperate thread, and
// therefore you are allowed to do action in main-gui-form while this work is done,
// due to async. operation.
// While work is done in seperate thread - the main-gui-form will have a label modified...
// having the steps: 1,2,3,0.
// Also found... "button2": will do some work in same thread as gui, and
// therefore you are not allowed to do action in main-gui-form while this work is done,
// due to sync. operation (only one thread is established).
// While work is done in one-and-only-thread - the main-gui-form will have a label modified...
// having the steps: 1,2,3,0.
{
public delegate void UpdateTextDelegate();
public partial class Form1 : Form
{
public delegate void SetStatusText(string statusText);
public SetStatusText mySetStatusTextDelegate;
public Form1()
{
InitializeComponent();
mySetStatusTextDelegate = new SetStatusText(SetStatusTextMethod);
}
private void button1_Click(object sender, EventArgs e) // do work from new thread.
{
Worker w = new Worker(this);
Thread thread1 = new Thread(new ThreadStart(w.DoWork));
thread1.Start();
}
private void button2_Click(object sender, EventArgs e) // do work from local class - form is locked during 1-3 steps.
{
SetStatusTextMethod("1");
Thread.Sleep(3000);
SetStatusTextMethod("2");
Thread.Sleep(3000);
SetStatusTextMethod("3");
Thread.Sleep(3000);
SetStatusTextMethod("0");
}
public void SetStatusTextMethod(string statusText)
{
label1.Text = statusText;
label1.Refresh();
}
}
public class Worker
{
Form1 guiForm; // holds form where "control-to-be-changes" is found.
public Worker(Form1 _guiForm)
{
guiForm = _guiForm;
}
public void DoWork() // while steps are being done - form can easily be moved around... is not locked!
{
// put "1/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "1");
Thread.Sleep(3000);
// put "2/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "2");
Thread.Sleep(3000);
// put "3/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "3");
Thread.Sleep(3000);
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "0");
}
}
}
While maintaining some code, I discovered that we have an infinite hang-up in a background worker. The worker requires access to a script file. The original code was written to pop up a file dialog if no script file was defined, to allow the user to select one. It looks something like this:
private void bgworker_DoWork(object sender, DoWorkEventArgs e)
{
... snip ...
if (String.IsNullOrWhitespace(scriptFile))
{
scriptFile = PromptForScript();
}
... snip ...
}
private string PrompForScript()
{
string script = "";
OpenFileDialog openDialog = new OpenFileDialog();
if (openDialog.ShowDialog() == DialogResult.OK)
{
script = openDialog.FileName;
}
return script;
}
I've read up a bit about MethodInvoker, but almost all of the invoke methods require that you call them from a control. The background worker in question is running from a separate class, which doesn't extend Control. Do I use the form that calls the class with the bgworker for that? Or is there another way of interrupting the thread for user input?
It's not recommended to invoke the UI from the background worker DoWork event handler. BackgroundWorker is meant to do work on a non-UI thread to keep the UI responsive. You should ask for any file information before starting the BackgroundWorker object with RunWorkerAsync.
What you want to do is capture the SynchronizationContext on the UI thread and pass that along to the background worker. The BackgroundWorker can call Send() (synchronous, like Invoke) and Post() (asynchronous, like BeginInvoke) on the context to invoke back to the correct UI thread. That said, there is probably no need for the BackgroundWorker in this case- a regular threadpool thread would do just fine.
This (slightly modified) block of code from http://msmvps.com/blogs/manoj/archive/2005/11/03/74120.aspx should give you the general idea:
private void button1_Click(object sender, EventArgs e)
{
// Here we are on the UI thread, so SynchronizationContext.Current
// is going to be a WindowsFormsSynchronizationContext that Invokes properly
ctx = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(
// This delegate is going to be invoked on a background thread
s => {
// This uses the context captured above to invoke
// back to the UI without the "messy" referencing
// of a particular form
ctx.Send(s2 =>
{
// Interact with your UI here- you are on the UI thread
},null);
}
);
}
If some Form kicks off a long-running process within another class that uses a BGworker, why wouldn't the form (or presenter, depending on UI architecture) handle the processing of the error state?
Perhaps, just pass back some status result (or throw a very targeted, specific exception that you can handle in the UI)?
Leave the background worker to determine if there IS an error, but leave handing the error (especially the UI portion of showing a message box) to the upper layers.
Sorry this didn't have more concrete code but it could go a lot of different ways depending on how your system is architected.
Well, the Form class has an Invoke method, so passing the form instance to the background working class should work.
I haven't done much multithreading before and now find the need to do some background work and keep the UI responsive. I have the following code.
data.ImportProgressChanged += new
DataAccess.ImportDelegate(data_ImportProgressChanged);
Thread importThread = new Thread(
new ThreadStart(data.ImportPeopleFromFAD));
importThread.IsBackground = true;
importThread.Start();
void data_ImportProgressChanged(int progress)
{
toolStripProgressBar.Value = progress;
}
//In my data object I have
public void ImportPeopleFromFAD()
{
ImportProgressChanged(someInt);
}
But the UI doesn't get updated since the ImportProgressChanged() call is made on the background thread. In objective C I know you can use performSelectorOnMainThread and pass it a method to call using the main thread. What is the equivalent way of calling ImportProgressChanged() from the main thread?
(Assuming Windows Forms.) You can use Control.Invoke or Control.BeginInvoke - but a cleaner way may be to use BackgroundWorker to start with.
In WPF you'd use a Dispatcher instead of Control.Invoke, btw. See this WPF threading model guide for more details.
EDIT: Personally I probably wouldn't bother testing InvokeRequired first - I'd just call Invoke or BeginInvoke. If you're already "on" the right thread it won't do any significant harm.
For progress bars, however, BackgroundWorker is definitely the way forward.
Instead of updating the GUI, data_ImportProgressChanged should throw an exception when it gets called changes the progressbar.
The shortest change is to use Control.InvokeRequired and .Invoke(), but the Backgroundworker was especially created for this scenario .
If you want to solve it in the data object you will have to make that depend on the GUI so its better to solve this in the handler:
void data_ImportProgressChanged(int progress)
{
if (toolStripProgressBar.InvokeRequired)
{
Action<int> a = new Action(data_ImportProgressChanged);
toolStripProgressBar.Invoke(a, progress);
}
else
toolStripProgressBar.Value = progress;
}
You already have your answer but I'll add my solution anyway:
void data_ImportProgressChanged(int progress)
{
if (InvokeRequired)
{
BeginInvoke(new Action<int>(data_ImportProgressChanged),progress);
return;
}
toolStripProgressBar.Value = progress;
}
I want to display a progress bar while doing some work, but that would hang the UI and the progress bar won't update.
I have a WinForm ProgressForm with a ProgressBar that will continue indefinitely in a marquee fashion.
using(ProgressForm p = new ProgressForm(this))
{
//Do Some Work
}
Now there are many ways to solve the issue, like using BeginInvoke, wait for the task to complete and call EndInvoke. Or using the BackgroundWorker or Threads.
I am having some issues with the EndInvoke, though that's not the question. The question is which is the best and the simplest way you use to handle such situations, where you have to show the user that the program is working and not unresponsive, and how do you handle that with simplest code possible that is efficient and won't leak, and can update the GUI.
Like BackgroundWorker needs to have multiple functions, declare member variables, etc. Also you need to then hold a reference to the ProgressBar Form and dispose of it.
Edit: BackgroundWorker is not the answer because it may be that I don't get the progress notification, which means there would be no call to ProgressChanged as the DoWork is a single call to an external function, but I need to keep call the Application.DoEvents(); for the progress bar to keep rotating.
The bounty is for the best code solution for this problem. I just need to call Application.DoEvents() so that the Marque progress bar will work, while the worker function works in the Main thread, and it doesn't return any progress notification. I never needed .NET magic code to report progress automatically, I just needed a better solution than :
Action<String, String> exec = DoSomethingLongAndNotReturnAnyNotification;
IAsyncResult result = exec.BeginInvoke(path, parameters, null, null);
while (!result.IsCompleted)
{
Application.DoEvents();
}
exec.EndInvoke(result);
that keeps the progress bar alive (means not freezing but refreshes the marque)
It seems to me that you are operating on at least one false assumption.
1. You don't need to raise the ProgressChanged event to have a responsive UI
In your question you say this:
BackgroundWorker is not the answer
because it may be that I don't get the
progress notification, which means
there would be no call to
ProgressChanged as the DoWork is a
single call to an external function .
. .
Actually, it does not matter whether you call the ProgressChanged event or not. The whole purpose of that event is to temporarily transfer control back to the GUI thread to make an update that somehow reflects the progress of the work being done by the BackgroundWorker. If you are simply displaying a marquee progress bar, it would actually be pointless to raise the ProgressChanged event at all. The progress bar will continue rotating as long as it is displayed because the BackgroundWorker is doing its work on a separate thread from the GUI.
(On a side note, DoWork is an event, which means that it is not just "a single call to an external function"; you can add as many handlers as you like; and each of those handlers can contain as many function calls as it likes.)
2. You don't need to call Application.DoEvents to have a responsive UI
To me it sounds like you believe that the only way for the GUI to update is by calling Application.DoEvents:
I need to keep call the
Application.DoEvents(); for the
progress bar to keep rotating.
This is not true in a multithreaded scenario; if you use a BackgroundWorker, the GUI will continue to be responsive (on its own thread) while the BackgroundWorker does whatever has been attached to its DoWork event. Below is a simple example of how this might work for you.
private void ShowProgressFormWhileBackgroundWorkerRuns() {
// this is your presumably long-running method
Action<string, string> exec = DoSomethingLongAndNotReturnAnyNotification;
ProgressForm p = new ProgressForm(this);
BackgroundWorker b = new BackgroundWorker();
// set the worker to call your long-running method
b.DoWork += (object sender, DoWorkEventArgs e) => {
exec.Invoke(path, parameters);
};
// set the worker to close your progress form when it's completed
b.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) => {
if (p != null && p.Visible) p.Close();
};
// now actually show the form
p.Show();
// this only tells your BackgroundWorker to START working;
// the current (i.e., GUI) thread will immediately continue,
// which means your progress bar will update, the window
// will continue firing button click events and all that
// good stuff
b.RunWorkerAsync();
}
3. You can't run two methods at the same time on the same thread
You say this:
I just need to call
Application.DoEvents() so that the
Marque progress bar will work, while
the worker function works in the Main
thread . . .
What you're asking for is simply not real. The "main" thread for a Windows Forms application is the GUI thread, which, if it's busy with your long-running method, is not providing visual updates. If you believe otherwise, I suspect you misunderstand what BeginInvoke does: it launches a delegate on a separate thread. In fact, the example code you have included in your question to call Application.DoEvents between exec.BeginInvoke and exec.EndInvoke is redundant; you are actually calling Application.DoEvents repeatedly from the GUI thread, which would be updating anyway. (If you found otherwise, I suspect it's because you called exec.EndInvoke right away, which blocked the current thread until the method finished.)
So yes, the answer you're looking for is to use a BackgroundWorker.
You could use BeginInvoke, but instead of calling EndInvoke from the GUI thread (which will block it if the method isn't finished), pass an AsyncCallback parameter to your BeginInvoke call (instead of just passing null), and close the progress form in your callback. Be aware, however, that if you do that, you're going to have to invoke the method that closes the progress form from the GUI thread, since otherwise you'll be trying to close a form, which is a GUI function, from a non-GUI thread. But really, all the pitfalls of using BeginInvoke/EndInvoke have already been dealt with for you with the BackgroundWorker class, even if you think it's ".NET magic code" (to me, it's just an intuitive and useful tool).
For me the easiest way is definitely to use a BackgroundWorker, which is specifically designed for this kind of task. The ProgressChanged event is perfectly fitted to update a progress bar, without worrying about cross-thread calls
There's a load of information about threading with .NET/C# on Stackoverflow, but the article that cleared up windows forms threading for me was our resident oracle, Jon Skeet's "Threading in Windows Forms".
The whole series is worth reading to brush up on your knowledge or learn from scratch.
I'm impatient, just show me some code
As far as "show me the code" goes, below is how I would do it with C# 3.5. The form contains 4 controls:
a textbox
a progressbar
2 buttons: "buttonLongTask" and "buttonAnother"
buttonAnother is there purely to demonstrate that the UI isn't blocked while the count-to-100 task is running.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonLongTask_Click(object sender, EventArgs e)
{
Thread thread = new Thread(LongTask);
thread.IsBackground = true;
thread.Start();
}
private void buttonAnother_Click(object sender, EventArgs e)
{
textBox1.Text = "Have you seen this?";
}
private void LongTask()
{
for (int i = 0; i < 100; i++)
{
Update1(i);
Thread.Sleep(500);
}
}
public void Update1(int i)
{
if (InvokeRequired)
{
this.BeginInvoke(new Action<int>(Update1), new object[] { i });
return;
}
progressBar1.Value = i;
}
}
And another example that BackgroundWorker is the right way to do it...
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace SerialSample
{
public partial class Form1 : Form
{
private BackgroundWorker _BackgroundWorker;
private Random _Random;
public Form1()
{
InitializeComponent();
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.Visible = false;
_Random = new Random();
InitializeBackgroundWorker();
}
private void InitializeBackgroundWorker()
{
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.DoWork += (sender, e) => ((MethodInvoker)e.Argument).Invoke();
_BackgroundWorker.ProgressChanged += (sender, e) =>
{
_ProgressBar.Style = ProgressBarStyle.Continuous;
_ProgressBar.Value = e.ProgressPercentage;
};
_BackgroundWorker.RunWorkerCompleted += (sender, e) =>
{
if (_ProgressBar.Style == ProgressBarStyle.Marquee)
{
_ProgressBar.Visible = false;
}
};
}
private void buttonStart_Click(object sender, EventArgs e)
{
_BackgroundWorker.RunWorkerAsync(new MethodInvoker(() =>
{
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(10);
_BackgroundWorker.ReportProgress(i / 10);
}
}));
}
}
}
Indeed you are on the right track. You should use another thread, and you have identified the best ways to do that. The rest is just updating the progress bar. In case you don't want to use BackgroundWorker like others have suggested, there is one trick to keep in mind. The trick is that you cannot update the progress bar from the worker thread because UI can be only manipulated from the UI thread. So you use the Invoke method. It goes something like this (fix the syntax errors yourself, I'm just writing a quick example):
class MyForm: Form
{
private void delegate UpdateDelegate(int Progress);
private void UpdateProgress(int Progress)
{
if ( this.InvokeRequired )
this.Invoke((UpdateDelegate)UpdateProgress, Progress);
else
this.MyProgressBar.Progress = Progress;
}
}
The InvokeRequired property will return true on every thread except the one that owns the form. The Invoke method will call the method on the UI thread, and will block until it completes. If you don't want to block, you can call BeginInvoke instead.
BackgroundWorker is not the answer because it may be that I don't get the progress notification...
What on earth does the fact that you're not getting progress notification have to do with the use of BackgroundWorker? If your long-running task doesn't have a reliable mechanism for reporting its progress, there's no way to reliably report its progress.
The simplest possible way to report progress of a long-running method is to run the method on the UI thread and have it report progress by updating the progress bar and then calling Application.DoEvents(). This will, technically, work. But the UI will be unresponsive between calls to Application.DoEvents(). This is the quick and dirty solution, and as Steve McConnell observes, the problem with quick and dirty solutions is that the bitterness of the dirty remains long after the sweetness of the quick is forgotten.
The next simplest way, as alluded to by another poster, is to implement a modal form that uses a BackgroundWorker to execute the long-running method. This provides a generally better user experience, and it frees you from having to solve the potentially complicated problem of what parts of your UI to leave functional while the long-running task is executing - while the modal form is open, none of the rest of your UI will respond to user actions. This is the quick and clean solution.
But it's still pretty user-hostile. It still locks up the UI while the long-running task is executing; it just does it in a pretty way. To make a user-friendly solution, you need to execute the task on another thread. The easiest way to do that is with a BackgroundWorker.
This approach opens the door to a lot of problems. It won't "leak," whatever that is supposed to mean. But whatever the long-running method is doing, it now has to do it in complete isolation from the pieces of the UI that remain enabled while it's running. And by complete, I mean complete. If the user can click anywhere with a mouse and cause some update to be made to some object that your long-running method ever looks at, you'll have problems. Any object that your long-running method uses which can raise an event is a potential road to misery.
It's that, and not getting BackgroundWorker to work properly, that's going to be the source of all of the pain.
I have to throw the simplest answer out there. You could always just implement the progress bar and have no relationship to anything of actual progress. Just start filling the bar say 1% a second, or 10% a second whatever seems similar to your action and if it fills over to start again.
This will atleast give the user the appearance of processing and make them understand to wait instead of just clicking a button and seeing nothing happen then clicking it more.
Here is another sample code to use BackgroundWorker to update ProgressBar, just add BackgroundWorker and Progressbar to your main form and use below code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Shown += new EventHandler(Form1_Shown);
// To report progress from the background worker we need to set this property
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
void Form1_Shown(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
}
// On worker thread so do our thing!
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100);
}
}
// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
}
refrence:from codeproject
Use the BackgroundWorker component it is designed for exactly this scenario.
You can hook into its progress update events and update your progress bar. The BackgroundWorker class ensures the callbacks are marshalled to the UI thread so you don't need to worry about any of that detail either.
Reading your requirements the simplest way would be to display a mode-less form and use a standard System.Windows.Forms timer to update the progress on the mode-less form. No threads, no possible memory leaks.
As this only uses the one UI thread, you would also need to call Application.DoEvents() at certain points during your main processing to guarantee the progress bar is updated visually.
Re: Your edit.
You need a BackgroundWorker or Thread to do the work, but it must call ReportProgress() periodically to tell the UI thread what it is doing. DotNet can't magically work out how much of the work you have done, so you have to tell it (a) what the maximum progress amount you will reach is, and then (b) about 100 or so times during the process, tell it which amount you are up to. (If you report progress fewer than 100 times, the progess bar will jump in large steps. If you report more than 100 times, you will just be wasting time trying to report a finer detail than the progress bar will helpfully display)
If your UI thread can happily continue while the background worker is running, then your work is done.
However, realistically, in most situations where the progress indication needs to be running, your UI needs to be very careful to avoid a re-entrant call. e.g. If you are running a progress display while exporting data, you don't want to allow the user to start exporting data again while the export is in progress.
You can handle this in two ways:
The export operation checks to see if the background worker is running, and disabled the export option while it is already importing. This will allow the user to do anything at all in your program except exporting - this could still be dangerous if the user could (for example) edit the data that is being exported.
Run the progress bar as a "modal" display so that your program reamins "alive" during the export, but the user can't actually do anything (other than cancel) until the export completes. DotNet is rubbish at supporting this, even though it's the most common approach. In this case, you need to put the UI thread into a busy wait loop where it calls Application.DoEvents() to keep message handling running (so the progress bar will work), but you need to add a MessageFilter that only allows your application to respond to "safe" events (e.g. it would allow Paint events so your application windows continue to redraw, but it would filter out mouse and keyboard messages so that the user can't actually do anything in the proigram while the export is in progress. There are also a couple of sneaky messages you'll need to pass through to allow the window to work as normal, and figuring these out will take a few minutes - I have a list of them at work, but don't have them to hand here I'm afraid. It's all the obvious ones like NCHITTEST plus a sneaky .net one (evilly in the WM_USER range) which is vital to get this working).
The last "gotcha" with the awful dotNet progress bar is that when you finish your operation and close the progress bar you'll find that it usually exits when reporting a value like "80%". Even if you force it to 100% and then wait for about half a second, it still may not reach 100%. Arrrgh! The solution is to set the progress to 100%, then to 99%, and then back to 100% - when the progress bar is told to move forwards, it animates slowly towards the target value. But if you tell it to go "backwards", it jumps immediately to that position. So by reversing it momentarily at the end, you can get it to actually show the value you asked it to show.
If you want a "rotating" progress bar, why not set the progress bar style to "Marquee" and using a BackgroundWorker to keep the UI responsive? You won't achieve a rotating progress bar easier than using the "Marquee" - style...
We are use modal form with BackgroundWorker for such a thing.
Here is quick solution:
public class ProgressWorker<TArgument> : BackgroundWorker where TArgument : class
{
public Action<TArgument> Action { get; set; }
protected override void OnDoWork(DoWorkEventArgs e)
{
if (Action!=null)
{
Action(e.Argument as TArgument);
}
}
}
public sealed partial class ProgressDlg<TArgument> : Form where TArgument : class
{
private readonly Action<TArgument> action;
public Exception Error { get; set; }
public ProgressDlg(Action<TArgument> action)
{
if (action == null) throw new ArgumentNullException("action");
this.action = action;
//InitializeComponent();
//MaximumSize = Size;
MaximizeBox = false;
Closing += new System.ComponentModel.CancelEventHandler(ProgressDlg_Closing);
}
public string NotificationText
{
set
{
if (value!=null)
{
Invoke(new Action<string>(s => Text = value));
}
}
}
void ProgressDlg_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
FormClosingEventArgs args = (FormClosingEventArgs)e;
if (args.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
}
}
private void ProgressDlg_Load(object sender, EventArgs e)
{
}
public void RunWorker(TArgument argument)
{
System.Windows.Forms.Application.DoEvents();
using (var worker = new ProgressWorker<TArgument> {Action = action})
{
worker.RunWorkerAsync();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
ShowDialog();
}
}
void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
Error = e.Error;
DialogResult = DialogResult.Abort;
return;
}
DialogResult = DialogResult.OK;
}
}
And how we use it:
var dlg = new ProgressDlg<string>(obj =>
{
//DoWork()
Thread.Sleep(10000);
MessageBox.Show("Background task completed "obj);
});
dlg.RunWorker("SampleValue");
if (dlg.Error != null)
{
MessageBox.Show(dlg.Error.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
dlg.Dispose();