How can I set ProgressBar and other command to start simultaneously? - c#

I write below code in click event of one button, and want ProgressBar & for loops start working together simultaneously. So at first I started timer1
private void Button_Click(object sender, EventArgs e)
{
this.timer1.Start();
if (comboBox.SelectedIndex == 0)
{
TextBox.Clear();
for (int j = 0; j < N; j++)
for (int i = 0; i < N; i++)
{
TextBox.Text += array[i, j].ToString()+" , " ;
}
}
}
But when I click button, first TextBox start to fill( for loops work), then the timer1 start to work and ProgressBar start to incrementing.
I use Visual Studio 2010, Windows Forms application and write bottom code for timer1:
private void timer1_Tick(object sender, EventArgs e)
{
this.ProgressBar.Increment(1);
}
Can every body tell how can I set them to start and work simultaneously?

You are using a System.Windows.Forms.Timer which runs on the UI thread and as you are blocking the UI thread it won't execute until your Button_Click method completes.
You must either update the ProgressBar from within your for loops directly (which will still block the UI thread and cause your application to be unresponsive for the duration) or to do it correctly you should create an asynchronously worker such as a System.Threading.Task or a System.ComponentModel.BackgroundWorker and implement it in such as way as to provide progress updates to the main thread which will update the ProgressBar as needed.
Here is a simple tutorial on how to use a BackgroundWorker which is probably the simpler solution.
You'll notice in this tutorial a method called bw_ProgressChanged which updates a TextBox. You would simply want to change this method to update your ProgressBar as well as your TextBox.

Windows Forms uses a single threaded message loop, so until your Button_Click hander has returned, the handler for the timer will not be able to run. Read up on the windows message loop to understand why.
The key here is that you should never perform long-running tasks in an event handler. You may have noticed that your window hangs until your code is running.
To get this to work, you could use a BackgroundWorker, or start a thread yourself. Also note that if you run your long-running task on another thread, it cannot access the form directly.
Check this old msdn article out.

Related

Update textbox during timer_tick

I have read many answers on this question, and yet I still cannot get this to work. I have a simple C# WinForms app with a timer control. When the timer fires, I have some code do some processing. I want to update a textbox with status during this processing. But the textbox never gets updated until the eventhandler finishes. Please tell me how I can get the textbox to update during the processing.
Here is my code:
My Form:
public Form1()
{
InitializeComponent();
timer1.Interval = 60000;
timer1.Tick += new EventHandler(CheckStatus);
timer1.Start();
}
private void CheckStatus(object Sender, EventArgs e)
{
// Set the caption to the current time.
textBox1.AppendText(DateTime.Now.ToString() + Environment.NewLine);
ProcessStatus();
}
private void ProcessStatus()
{
textBox1.AppendText("Now updated" + Environment.NewLine);
}
If I step through my code, the textbox is not updated until I step out of CheckStatus. (I'm using Visual Studio 2017)
I have tried several things like what is found here: StackOverflow
When the timer ticks it's firing on the GUI thread. While the GUI thread is busy processing (I assume whatever you're doing takes a long time) all other GUI updates will pause.
You can run textBox.Update() to force the update at that point, but that's not considered a best practice.
Instead, you should run your process on a background thread. One option is BackgroundWorker and use the ProgressChanged event to show your updates in your GUI.

Is there a way to slow down a BackgroundWorker in WPF?

In some games, there is a splash screen that downloads content from a server. It might give some tips while you're waiting. I'm doing something similar, only that the loading happens really quick, but I want it to wait a few more seconds.
When the user first loads my application, it has a screen with a progress bar. At the moment, it checks if the server is online. If it is, it says "Connected!" However, it immediately fades out my controls. I want it to wait about 5 more seconds so the reader can read it. Then fade the controls out.
private void frmMain_Loaded(object sender, RoutedEventArgs e)
{
// Start background worker
m_bgWorker = new System.ComponentModel.BackgroundWorker();
m_bgWorker.ProgressChanged += M_bgWorker_ProgressChanged;
m_bgWorker.WorkerReportsProgress = true;
m_bgWorker.RunWorkerAsync();
m_bgWorker.ReportProgress(100);
}
private void M_bgWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progConnect.Value = 0;
if (SystemBO.IsOnline())
{
lblConnection.Content = "Connected!";
}
progConnect.Value = e.ProgressPercentage;
// TODO: Wait 5 seconds here...
// Fade out controls
lblTitle.BeginAnimation(Label.OpacityProperty, doubleAnimation);
progConnect.BeginAnimation(Label.OpacityProperty, doubleAnimation);
lblConnection.BeginAnimation(Label.OpacityProperty, doubleAnimation);
}
Note:
I tried System.Sleep(), but that made no difference. I understand why. The idea is the same though: I want the background worker to sleep for 5 seconds before completing.
Solution:
I added a few more events: DoWork and RunWorkerCompleted.
Then I added this code:
private void M_bgWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
m_index++;
System.Threading.Thread.Sleep(40);
m_bgWorker.ReportProgress(m_index);
}
System.Threading.Thread.Sleep(3000); // wait 3 seconds to read
}
It worked. The progress bar animates quite smoothly.
You shouldn't do the fade-out in the ProgressChanged handler. That handler really should only be used to update progress information like text and a percentage bar.
Your fade-out logic should be moved to the separate RunWorkedCompleted event handler. Here you can let the thread sleep/await a scheduled task for a while before beginning to fade out the controls. (Using async/await over Thread.Sleep() here has the advantage of not blocking the UI thread while waiting, so the UI still remains responsive while waiting for the time to pass.)
It also ensures that the code is called at the very end and not every time the worker reports any kind of progress. And it's also cleaner because it allows you to handle different termination states by checking the RunWorkedCompletedEventArgs:
If e.Error is set, an exception occurred in the worker thread.
If e.Cancelled is true, the worker thread got cancelled by calling CancelAsync() on the worker. (Only possible if WorkerSupportsCancellation is set to true and only useful if code in the DoWork handler actually checks for the cancellation flag)
Otherwise everything went okay.
Side note:
I am assuming that your code is not a full example, because you have no actual handler assigned to the DoWork event. So right now it would not do anything. And calling ReportProgress() right from the main thread is wrong as well. That method is designed to be called from within the DoWork event handling method in order to let the asynchronous thread report status updates back to the UI, since those events are handled on the main thread.
rather than the last line in frmMain_Loaded:
m_bgWorker.ReportProgress(100);
I would put a for loop with Sleep(), for simulating some extra processing:
for (int i=0; i<=100; i+=10)
{
if (SystemBO.IsOnline())
i = 100;
Sleep(1000);
m_bgWorker.ReportProgress(i);
}

How can I stop a long-running subroutine in my Form with a Button?

I have a Console app that displays a WinForms Form.
In the Form, the user clicks button 1 and it runs a long subroutine. I want to have a button 2 that can kill the subroutine at any point. However, the UI freezes when I click button 1 until the subroutine has finished. How can I get the UI to not freeze?
Your long-running code is blocking the UI thread, so you can no longer click the second button, nor interact with the UI in any way until the code is finished executing.
You'll need to move your long-running code to a separate thread. There are various (and newer) ways of doing this, but one way is the BackgroundWorker. It's pretty easy to learn, and wraps some nice functionality, like cancelling the thread.
Here's a short WinForms app to demonstrate. You have to explicitly enable the ability for the thread to be cancelled. In this example, the while loop continues indefinitely, but checks every 100ms to see if there's a request for it to be cancelled. When you click the second button, the cancellation request is sent, and the thread ends.
public partial class Form1 : Form
{
private BackgroundWorker bg;
public Form1()
{
InitializeComponent();
bg = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
bg.DoWork += (sender, args) =>
{
while (true)
{
Thread.Sleep(100);
if (bg.CancellationPending)
break;
}
MessageBox.Show("Done!");
};
}
private void button1_Click(object sender, EventArgs e)
{
bg.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
bg.CancelAsync();
}
}
Following up on chouaib's comment, another nice thing about using the BackgroundWorker in a WinForms environment is that you can drop and drop it onto your designer, similar to a Menu, Timer, etc. You can then access its members in the "properties" panel, setting "WorkerSupportsCancellation" to true, subscribing to events, etc.
From your comment:
"is there a way to run this background process and be able to update the main userform? I keep getting the "cross-thread operation not valid control accessed from a thread other than the..." I want to run the long-running background operation, and have it update the main UI with text in a label (like a percentage of its progress)"
If you want to update the UI while the thread is running, you should do that from the ProgressChanged event. First, enable that option and subscribe to the event:
bg.WorkerReportsProgress = true;
bg.ProgressChanged += bg_ProgressChanged;
Then call ReportProgress() when you want to update the UI. You could pass back a percentage complete and some text, for instance:
bg.ReportProgress(50, "Almost there...");
Finally, update the UI from inside the event:
void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var message = e.UserState.ToString();
var percent = e.ProgressPercentage;
lblStatus.Text = message + " " + percent;
}
You need to make it multithreaded as suggested in the comments. The older way of doing this was manage your own thread. Then along came the background worker (cheap and easy). Now a days you have other options such as the Task Library.
Remember - anything the runs on the UI thread prevents the UI from sending and receiving events until that operation is finished.
Look into the BackgroundWorker component

C# windows form progress bar with a function on background worker

I am attempting to add a progress bar (MARQUEE style) in my c# excel add in. The progress bar appears but it does not indicate any progress until the function is finished executing, meaning that it remains frozen.
These are the functions in the Windows Form Class:
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
//run in back thread
backgroundWorker1.ReportProgress(i);
}
}
//call back method
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
//call back method
public void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Value = progressBar1.Maximum;
}
This is how I call the function from the add in button:
private void buttonClicked(object sender, RibbonControlEventArgs e)
{
AddInPlanView.Form1 pBar = new AddInPlanView.Form1();
pBar.Visible = true;
pBar.backgroundWorker1.WorkerReportsProgress = true;
pBar.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(pBar.backgroundWorker1_ProgressChanged);
pBar.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(pBar.backgroundWorker1_DoWork);
pBar.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(pBar.backgroundWorker1_RunWorkerCompleted);
pBar.backgroundWorker1.RunWorkerAsync();
FUNCTIONTORUN();
pBar.Visible = false;
}
My code now attempts to put the progress bar on a background worker. I know I should switch this so that FUNCTIONTORUN() should be on a background worker, but how do I go about doing that? Since it is a marquee progress bar I do not need to update the value of the progress bar at all, it just needs to run until FUNCTIONTORUN() ends. Note that FUNCTIONTORUN() updates the cells of an Excel worksheet.
The progress bar is initialized using the Visual Studio designer. The name of the ProgressBar object in the form class is progressbar1.
to start a marquee style progress bar, type
progressbar1.Style = ProgressBarStyle.Marquee;
progressbar1.MarqueeAnimationSpeed = 100;
to stop the marquee type
progressbar1.MarqueeAnimationSpeed= 0;
Source: http://social.msdn.microsoft.com/forums/en-US/winforms/thread/9e51ad57-988e-4c17-98f0-cd2a8abec503/
FUNCTIONTORUN(); // make your UI thread busy.
System.Windows.Forms.Application.DoEvents(); // might help in this case.
In most of this sort of cases DoEvent doesn't help (according to my experience). So, it's quite difficult to get process bar to work smoothly.
When the Background worker completes just stop the progress bar.
Inside of this function pBar.backgroundWorker1_RunWorkerCompleted just call the Stop function for the progress bar.
I believe you have to put FUNCTIONTORUN() into sepratae thread, and still it's execution call
ReportProgress method, in order to raise backgroundWorker1_ProgressChanged where you can update your porgress bar value. I suppose, as I don't see it in the code provided, you don't call ReportProgress function somewhere in the code.
EDITED: saw ReportProgress function.
So move your function into separate thread and update the value of progress bar into the main thread.
Hope this helps.
Regards.
Your FUNCTIONTORUN() blocks the UI thread.
Spread some System.Windows.Forms.Application.DoEvents() inside your long-running functions to process events (so the UI updates).
This, as mentioned, will update the UI and let the application respond to other events.
You might want to disable the button that triggers this long-running function, or other long-running functions.
You could create an additional background worker with a DoWork that contains the call to your FUNCTIONTORUN() inside of it and a RunWorkerCompleted that makes your bar stop or go invisible. Then, instead of calling FUNCTIONTORUN(), call backGroundWorker2.RunWorkerAsync();
This should prevent the function from freezing your UI. I ran into this issue last summer doing nearly the exact same thing, and fixed it using backgroundworkers.

Why is Form.Refresh() not working?

I'm running a data import, using a Windows form to kick off the import and show progress.
I've got this whole thing so nice and user friendly, with major and minor progress bars and everything... but just one problem... the form refresh keeps going AWOL.
I have a call to Form.Refresh() every time I update my labels/progress bars, and it usually starts off working. But if ever I need to break into debug mode, just to hand-hold the import a bit, the Refresh() call stops working, and sometimes even if I'm running without Debug mode, at some unpredictable point the same thing happens: the labels and progress bars do not get updated, and if you hide the form and reopen it, the form does not repaint at all - it just shows up entirely white.
Why, oh why, does Form.Refresh() stop working, and how can I fix this?
It sounds as if the import runs on the UI thread, which means that this thread is blocked, preventing the form from repainting itself. A better approach would be to use a BackgroundWorker component, do the import in the DoWork event handler and use the ProgressChanged to update the UI.
Example:
private void StartImport()
{
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.RunWorkerAsync();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// do some work simulating a lenghy process which occasionally
// reports progress with data back to the caller
for (int i = 0; i < 100; i++)
{
Thread.Sleep(200);
backgroundWorker.ReportProgress(i, "Item No " + i.ToString());
}
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBox.Items.Add(e.UserState.ToString());
}
Using this approach you will typically not need to call Refresh to force a repaint of the form.
You may want to change your code into using BeginUpdate and EndUpdate, like so:
Control.BeginUpdate();
// Do something to the control, e.g. add items or whatnot
Control.EndUpdate();
This way Refresh shouldn't be necessary.
AFAIK constantly calling Refresh is really a hack and should be avoiding, as it stresses the CPU quite a bit (it has to refresh everything instead of just the things which are changed).
Edit: If the form starts being white, it seems the drawing code is not been called at all, which indicates it's somewhat not responding.
I'd check the code for anything that can deadlock or otherwisely hang.
You could use observer pattern..in short if anything changes in model observer pattern will make sure that change is visible on form..
google it for some examples..
Depending on what .NET framework you're using, you can use the Task.Run approach:
private void btnShowProgress_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = i; }));
}
});
}
Task.Run info
Using invoke with controls
The solution may not be the best practice but it definitely works for small applications.
In Form1 create a bool to check to see if the form is closed.
public bool formclosed = false
Then in Form2 on the Form Closing Event Handler add
formclosed = true
also in the Form2 after
InitializeComponent();
add
formclosed = false;
In Form1 create a timer.
In the timer1.Tick event handler say
private void timer1_Tick(object sender, EventArgs e)
{
if(formclosed == true)
{
Application.Restart();
}
}
This will restart the application and refresh everything ... I also had my text saved to the Properties.Settings.Default so everytime the application started the default settings would show.
I created an initial version of a Progress control using a BackgroundWorker. The Progress control computed and displayed nice things like Estimated Duration, Estimated Time to Completion. Each statistic was displayed by custom control based on a Label control. The Progress control worked in my production code.
Then I made some changes to the Progress control. I switched from basing my custom statistics controls from Label to Panel. I successfully ran all my tests (using NUnit). Then, I created a Test Windows Forms app. The Progress control successfully worked in the Test Windows Forms app.
When I ran my production Windows app with the updated Progress control it didn't display the statistics.
I tried Thread.Sleep(N), Form.Refresh(). None of those worked to update the statistics.
I eventually called Control.Invalidate() on each Statistic control. The Invalidate caused OnPaint to be called and the control happily updated the display with its specific statistic.
For Each Stat_Obj As Control in Statistics_Controls
Stat_Obj.Invalidate()
Next
You might need to give the window time to redraw itself. I understand you're doing the import in a loop and the loop is running on the main UI thread? Try adding this line to the loop:
Application.DoEvents();

Categories

Resources