Show progress bar while doing some work in C #?

I want to display a progress bar when doing any work, but this will cause the user interface to freeze and the progress bar will not be updated.

I have a WinForm ProgressForm with a ProgressBar that will continue indefinitely in the chalet .

 using(ProgressForm p = new ProgressForm(this)) { //Do Some Work } 

Now there are many ways to solve the problem, for example, using BeginInvoke , wait for the task to complete and call EndInvoke . Or use BackgroundWorker or Threads .

I'm having some problems with EndInvoke, although this is not a question. The question is which one is the best and easiest way to use such situations, when you have to show the user that the program is working and not reacting, and how do you deal with the simplest code that is effective and won, t and can update the GUI.

Like BackgroundWorker , you need to have several functions, declare member variables, etc. You also need to save the link to the ProgressBar form and get rid of it.

Edit : BackgroundWorker not the answer, because it may happen that I do not receive a progress notification, which means that there will be no call to ProgressChanged , since DoWork one call to an external function, but I need to continue calling Application.DoEvents(); so that the progress bar keeps spinning.

The bounty is the best solution to this problem. I just need to call Application.DoEvents() for the Marque progress bar to work and the work function to work in the main thread and not return any progress notification. I never need the .NET code to automatically report progress, 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); 

which keeps the progress bar alive (means that it does not freeze, but refreshes the brand)

+24
multithreading c # begininvoke
Dec 23 '09 at 11:21
source share
13 answers

It seems to me that you are working with at least one false assumption.

1. You do not need to raise a ProgressChanged event to have a responsive interface

In your question you say the following:

BackgroundWorker is not an answer because it may be that I am not receiving a progress notification, which means there would be no call to ProgressChanged as DoWork is one call to an external function.,.

Actually, it doesn't matter if you raise the ProgressChanged event or not . The whole purpose of this event is to temporarily transfer control back to the GUI thread to make an update that somehow reflects the progress of the work that BackgroundWorker is doing. If you just show the marquee progress bar, it would actually be pointless to raise the ProgressChanged event at all . The progress bar will continue to rotate while being displayed because BackgroundWorker is doing its work on a separate thread from the GUI .

(On the side, the DoWork note is an event, which means it's not just “one call to an external function”; you can add as many handlers as you want, and each of these handlers can contain as many function calls as he likes.)

2. You do not need to call Application.DoEvents to have a responsive interface

It seems to me that you think that the only way to update the GUI is to call Application.DoEvents :

I need to keep calling Application.DoEvents (); for progress bar to continue rotation.

This is not true in a multi-threaded scenario ; if you use BackgroundWorker , the GUI will continue to respond (in its own thread), and BackgroundWorker does everything that was 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 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 (ie, 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 cannot run two methods simultaneously in the same thread

You say this:

I just need to call Application.DoEvents () so that the Marque Indicator will work while the working function is working basically the thread.,.

What you are asking for is simply not real . The "main" thread for a Windows Forms application is a GUI thread that, if busy with your long-term method, does not provide visual updates. If you think otherwise, I suspect that you misunderstood what BeginInvoke doing: it starts the delegate in a separate thread . In fact, the sample code that you included in your question to call Application.DoEvents between exec.BeginInvoke and exec.EndInvoke is redundant; you are actually calling Application.DoEvents from the GUI thread , which will be updated anyway . (If you find otherwise, I suspect this because you immediately called exec.EndInvoke , which blocked the current thread until the method completed.)

So yes, the answer you are looking for is to use BackgroundWorker .

You can use BeginInvoke , but instead of calling EndInvoke from the GUI thread (which will block it if this method is not finished), pass the AsyncCallback parameter to your BeginInvoke call (instead just pass null ) and close the execution form in your callback. However, keep in mind that if you do this, you will have to call a method that closes the progress form from the GUI thread, because otherwise you will try to close the form, which is a GUI function, from the stream without a GUI. But in fact, all the problems with using BeginInvoke / EndInvoke have already been considered for you with the BackgroundWorker class, even if you think this is "magic .NET code" (for me it is just an intuitive and useful tool).

+41
Dec 30 '09 at 2:31
source share

For me, the easiest way is to use BackgroundWorker , which is specially designed for this kind of task. The ProgressChanged event is great for updating the progress bar without worrying about cross-thread calls

+16
Dec 23 '09 at 11:27
source share

There loading information about streaming using .NET / C # in Stackoverflow, but the article that cleared windows to handle streams for me was our resident oracle, John Skeet "Threading in Windows Forms" .

The entire series is worth reading to refresh your knowledge or learn from scratch.

I'm impatient, just show me the code

Regarding the "show me code", below, as I would do with C # 3.5. The form contains 4 controls:

  • text field
  • progress indicator
  • 2 buttons: "buttonLongTask" and "buttonAnother"

buttonAnother exists purely to demonstrate that the user interface is not blocked when the "account-100" task is run.

 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; } } 
+10
Dec 23 '09 at 12:09
source share

And another example that BackgroundWorker is the right way to do this ...

 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); } })); } } } 
+8
Dec 28 '09 at 15:17
source share

Indeed, you are on the right track. You must use a different thread, and you have identified the best ways to do this. The rest just updates the progress bar. If you don't want to use BackgroundWorker, as others have suggested, there is one trick to keep in mind. The trick is that you cannot update the progress bar from the workflow because the user interface can only be processed from the user interface thread. So you are using the Invoke method. This happens something like this (correct 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 for each stream except the one that owns the form. The Invoke method will call the method in the user interface thread and will block until it finishes. If you do not want to block, you can call BeginInvoke instead.

+3
Dec 23 '09 at 11:32
source share

BackgroundWorker not the answer, because it may happen that I do not receive a progress notification ...

What is due to the fact that you are not getting progress notifications related to using BackgroundWorker ? If your long-term task does not have a reliable mechanism for reporting its progress, there is no way to reliably report its progress.

The easiest way to report the progress of a long-term method is to run the method in the user interface thread and report its progress by updating the progress bar and then calling Application.DoEvents() . This will technically work. But the user interface will not answer calls to Application.DoEvents() . This is a 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 method referenced by another poster is to implement a modal form that uses BackgroundWorker to execute a long-term method. This provides, as a rule, a better user interface, and it eliminates the need to solve the potentially complex problem of which parts of your user interface are left functional while a lengthy task is performed - although the modal form is open, none of the rest your user interface will respond on user actions. This is a quick and clean solution.

But he is still quite aggressive. It still blocks the user interface when lengthy work is done; he just does it beautifully. To make a user-friendly solution, you need to complete the task in another thread. The easiest way to do this is BackgroundWorker .

This approach opens the door to many problems. This is not a "leak", whatever that implies. But no matter what the long method does, it should now do it in complete isolation from the parts of the user interface that remain enabled during operation. And upon completion, I mean complete. If the user can click anywhere and cause some update for any object that you have ever encountered, you will have problems. Any object that uses your long method that can raise an event is a potential road to suffering.

This is that, without getting BackgroundWorker to work properly, it will become the source of all the pain.

+2
Dec 28 '09 at 19:33
source share

I need to skip the simplest answer. You can always simply implement the progress bar and have nothing to do with any of the actual progress. Just start filling the bar, say, 1% per second, or 10% per second, which is similar to your action, and if it fills, to start again.

This, at least, will give the user the look of the processing and make them understand, to wait, and not just click on the button and not see anything, and then click on it more.

+1
Dec 28 '09 at 19:04
source share

Here is another sample code to use BackgroundWorker to update the ProgressBar , just add BackgroundWorker and ProgressBar to the main form and use the code below:

 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

+1
Jul 27 '16 at 16:18
source share

Use the BackgroundWorker component, which is designed specifically for this scenario.

You can connect to your progress update events and update the progress bar. The BackgroundWorker class ensures that callbacks are redirected to the user interface thread, so you don't need to worry about any of these details.

0
Dec 23 '09 at 11:27
source share

We use a modal form with BackgroundWorker for such a thing.

Here is a 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 do 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(); 
0
Dec 23 '09 at 11:30
source share

Reading your requirements, the easiest way would be to display a form without a mode and use the standard System.Windows.Forms timer to update progress in a form without a mode. No threads, no possible memory leaks.

Since this uses only one thread of the user interface, you also need to call Application.DoEvents () at certain points during the main processing to ensure that the progress bar is updated visually.

0
Dec 23 '09 at 11:35
source share

Re: Your editing. You need BackgroundWorker or Thread to do the job, but it must call ReportProgress () periodically to tell the user interface thread what it is doing. DotNet cannot magically determine what part of the work you have done, so you must tell him (a) what is the maximum level of progress that you will achieve, and then (b) about 100 or so during the process, tell me what amount you are counting on. (If you report progress less than 100 times, the progess panel will jump in large steps. If you report more than 100 times, you will simply waste time reporting the smallest details than the progress indicator shows)

If your UI thread can happily continue to work as a background worker, then your work will be done.

However, realistically, in most situations where a progress bar should be triggered, your user interface should be very careful to avoid calling again. for example, if progress is displayed during data export, you do not want the user to start exporting data again during export.

You can handle this in two ways:

  • The export operation checks if the background desktop is working and has disabled the export while it is already importing. This will allow the user to do anything at all in your program, except for export - this can be dangerous if the user can (for example) edit the exported data.

  • "" , "" , ( ) . DotNet - , , . , Application.DoEvents(), ( ), MessageFilter, "" (, Paint, , , , , , - , , , NCHITTEST .net ( WM_USER), , ).

"gotcha" dotNet , , , , , "80%". 100%, , 100%. Arrrgh! , 100%, 99%, 100% - , , . , "", . , , , .

0
28 . '09 18:50
source share

"" , "Marquee" BackgroundWorker , ? , "Marquee"...

0
30 . '09 12:31
source share



All Articles