C# progress bar doesn't show WinForm - c#

What happens is when I click the button to run my code my win form locks up and my progress bar doesn't run. What am I doing wrong?
foreach (string barcount in idit)
{
barcountmax++;
}
label2.Text = "Total entries: " + barcountmax;
progressBar1.Minimum = 0;
progressBar1.Maximum = barcountmax;
...
foreach (string ids in idit)
{
string[] splitids = ids.Split('|');
string fixcommacrap = "";
barmovement++;
progressBar1.Value = barmovement;
}
My Winform just locks and freezes, i'm unable to see the progress bar.

My guess is that you're doing all the work in the UI thread. Don't do that.
Use BackgroundWorker to do the work, periodically updating the progress bar appropriately, using ReportProgress to allow the UI change to be made in the UI thread.
If you search for BackgroundWorker tutorials you'll find a whole bunch of them, such as this one. Many will give examples using progress bars, as BackgroundWorker is pretty much designed for that scenario.

By default if you are running a long operation the winform is not going to repaint itself. To fix it consider:
Running your computations on a separate thread, preferably using BackgroudWorker
Repainting it manually every time you change your progress bar.
(1) is strongly recommended.

For that to work, you need to let the form paint when necessary. The correct / preferred way is to use a worker thread (perhaps BackgroundWorker) for the long running tasks, calling back to the UI thread periodically to ask it to update the progress.
There is also DoEvents, but that is (frankly) a hack.

The easiest but not correct way to fix the issue is to use the following inside your loop:
Application.DoEvents();
The more challenging is to use separate thread for calculations and current UI thread for updating the progress bar.

As suggested, use a BackGroundWorker.
In the worker, DO NOT update the ProgressBar directly, do it with BeginInvoke.

Related

Implementing a Progress Bar in C#

Attempt number 2. Trying to be as clear as possible because I have been on this task for much longer than I should have and made little progress.
I need to make a progress bar for an application. Right now, everything is happening in one UI thread, all the processing and whatnot, so when I do the long running process after a click of a button, the program hangs for roughly 40 seconds, then resumes with the output (I cannot change that part, the application was given to me). And I also have to make a cancel button, so if it is hit during the intermediate process, after that process is done, it checks for the Cancel flag, and if it is ON, break out of all the methods and return to initial position.
What I have to do is make a progress bar for this process. Two of them, one for intermediate process and one for total. Intermediate is a small action, like DB call, output intermediate process (for and foreach loops).
I have tried putting the bars in the same UI window and update them in the same thread as I go on (find major points, start the progress bars there and increment them based on my judgement. And loops). That was just loading the UI even more, but it was working (choppy progress bar movements).
Then I tried using the background worker. Same thing, put the bars in the UI and run them through the background worker. The problem with that even though the BG worker runs, the refresh must happen in the UI window, which is busy processing the greedy request, so it is not updated when I want it to.
I tried using multi threading at core (no BG worker) and create a new form in a new thread that would reflect the progress, but I was cautioned not to do it (running 2 UI threads at the same time) and I had troubles passing values around. Not to mention if using the threading method it is impossible to get an accurate representation of the progress happening without completely overloading the CPU (have another thread run in a while(true) and do an update when you call the updateMethod from some point in the process)
Do I have any other options left? Because the least negative aspect is the first where I update the progress bar as I go along with the program.
Do I have any other options left?
Yes and no. The best option is still to put time to learn how to use the backgroundworker properly, it's by far the easiest solution for this problem.
Go ahead and study these links:
Official MSDN Backgroundworker
Step by Step Tutorial Number 1 (dotnetperls)
Step by Step Tutorial Number 2 (dreamincode)
Most important thing is to set this property:
bw.WorkerReportsProgress = true;
And in the DoWork-Function, make sure you report the progress to the UI, as given in the linked examples:
worker.ReportProgress((i * 10));
Also, don't forget to handle the progresschanged event in the UI:
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
Go ahead, and you will see: it is fairly simple.
When using a background worker you should be doing all of the non-UI processing in the background worker; you should not be using it to update the UI while still doing processing in the UI thread.
By moving the work to the DoWork handler of the BGW you free up the UI thread to allow it to process UI events, including the updating of the progress bar.
private void btnCreate_Click(object sender, EventArgs e)
{
Progressbar1.Minimum = 1;
Progressbar1.Maximum = dt.Rows.Count;
Progressbar1.Value = 1;
Progressbar1.Step = 1;
for (int i = 1; i <= dt.Rows.Count; i++)
{
bool result=InsertUserdetails(Username);
if(result== true)
{
Progressbar1.PerformStep();
}
}
}

Progress dialog on a heavy loaded UI thread does not get updated when binding changes

While maintaining an old MFC application we have implemented a new progress dialog bar in WPF. The application currently has the UI thread busy with a lot of business operations but changing this is out of scope.
When a string property changes its value (binded to the text of a TextBox) the progress dialog does not get refreshed (only sometimes when the thread is not so busy).
As far as I know as the update of the property is done from the UI thread the thread should be able to update the dialog and repaint it before going on the next thing so I don't get why it's not being updated and how to fix it.
Any ideas?
EDIT: What are the drawbacks of this solution, I have tried it and seems to work fine:
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
I found out the solution here:
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
Then I created a new thread with the progress dialog.
The application currently has the UI thread busy with a lot of business operations
Well there's your problem. You shouldn't be doing that. You ought to be performing long running operations in a non-UI thread. It's the reason why updates to the UI aren't made until after the long running operation finishes.
You can use a BackgroundWorker to help simplify interactions with a UI while performing a long running task, as it will handle marshaling to the UI thread for the progress updated and completed event handlers.
First, I agree with #Servy, you shouldn't do heavy work in the UI thread.
However, if you cannot do the work in another thread, you can consider spawning another one for your dialog. I don't know how you are calling the wpf window, but this link may give you some clues about how it would be done in C#.

multithread : new form close too fast

I want to show a new form with a progress bar marquee. This form should be on an other thread, since the action that is called is too big and the UI doesn't update the progress bar if it's on the same thread.
I call a new thread, create my form, but as soon as it appears, the form close...
Here is my code from my main :
frmProgress f = new frmProgress();
Thread t = new Thread(new ThreadStart(delegate() { f.Show(); }));
t.Start();
documentManager.Compile(); //This is my big action
t.Abort();
Here is the code of form :
public frmProgress()
{
InitializeComponent();
}
I would like to know what I did wrong.
Thanks!
You have several problems:
You're showing a form in a non-UI thread. This will only cause problems; it needs to be in the UI thread.
You're performing a long running task in the UI thread; this is blocking UI updates.
You need to be showing the forum in the UI thread, and running the long running task in a background thread.
A great tool for the job here is to use a BackgroundWorker. It will do much of the work for you to ensure the proper code is running in the UI/non-UI threads as it should be. Create a new BackgroundWorker and put the long running task in the DoWork event. Make any simple UI updates that you need to (showing the form) before you start the BGW, and do any cleanup in the Completed event (which will run in the UI). The MSDN page on BackgroundWorker has some nice examples.
Another advantage of using a BackgroundWorker is that you can easily report progress (if you have a good way of knowing your % complete) rather than just having a marquee bar. Just add a handler to the ProgressReported event handler to update the progress bar, and call the ReportProgress method periodically when doing work. See the linked examples for exact code snippets.

C# - Code processing order - strange behaviour

I have the follow button click event:
private void btnRun_Click(object sender, EventArgs e)
{
label1.Visible = true;
if (SelectDatabase())
{
if (string.IsNullOrEmpty(txtFolderAddress.Text))
MessageBox.Show("Please select a folder to begin the search.");
else
{
if (cbRecurse.Checked == false || Directory.GetDirectories(initialDirectory).Length == 0)
{
CheckSingleFolder();
}
else
{
CheckSingleFolder();
directoryRecurse(initialDirectory);
}
}
}
}
Effectively, it does a few checks and then starts some directory recursion looking for specific files. However, the very first line of code to make the label visible does not occur until after the directories have been recursed? Anybody know why this would be?
Thanks.
You're doing everything within the UI thread, which is a really bad idea - the UI doesn't get to update, react to events etc until you've finished.
You should use a background thread and update the UI with progress etc using Control.BeginInvoke, or perhaps use a BackgroundWorker.
Basically, there are two golden rules in WinForms (and similar with WPF/Silverlight):
Don't do anything which can take a significant amount of time in the UI thread
Don't touch any UI elements from any thread other than the UI thread
your whole method runs as a blocking unit currently - add an Application.DoEvents() as a workaround, but really you should be doing this kind of processing in a background thread, i.e. using a background worker.
The code is executing on the same thread which is drawing your user-interface. Therefore, while the code is executing, your UI is not being re-drawn. Once the button-click code has finished, the UI is redrawn and label1 gets drawn invisibly.
You can move your code into a separate thread using, for example, Task or BackgroundWorker. However, you cannot directly set UI properties from a different thread, so you will need to either be careful to set your UI properties from the UI thread or see this question about how to update the GUI from another thread.
The view is not updated until the code block finished. So I would propose a BackgroundWorker for the recursion part.
The explanation: the label is set to visible and it it is Invalidated (needs repainting) but the Windows message pump doesn't start repainting until it is running idle. So your code blocks it.
A simple solution is to call label1.Update() right after setting it visible.
A better solution is to move the time-consuming code to a thread (Backgroundworker).

WinForm And Looping

I have a WinForm set up and a process that loops until a button is pressed on the form.
When I try to run my code, the form does not even display. I suspect this is because the code gets stuck in the loop and doesn't get far enough to display the WinForm. How can I get the form to display and the loop to run after that point?
If you're looping because you need to do something with the GUI periodically while waiting for input, I suggest using a Timer control and its Tick event.
If you want to do non-GUI things while waiting, a more traditional timer is better suited to the task,
You should probably run the loop in a background thread. The BackgroundWorker class makes this pretty easy to do.
Don't do that.
Windows Forms (like most modern user interface development toolkits) is an event-driven framework. You should never use a loop that "waits" for something to happen; instead you want to use an event that triggers something to happen.
Essentially what's happening is this: WinForms has a loop running a message pump that listens for events from Windows and triggers C# events in response to them. Your code is executing on the same thread as that message pump (it has to, since in WinForms only one thread is allowed to touch any given control). So if you put that thread into a loop, the WinForms code that should be pumping messages isn't, and your user interface appears to hang, since it isn't responding to any messages from Windows. (If you keep clicking it, you will fill up the message queue and get a dialog box that says "This application has stopped responding, do you want to terminate?" or something like that.)
The correct solution is to do one of the following:
Use a Timer
Use a BackgroundWorker
Use a ThreadPool
Another solution that would work, but is not a good idea is:
Use Application.DoEvents() -- but please don't actually do this
Your form loading is freezing because the UI of a windows form runs in a single thread. And the logic that you put on the Load event of this form is running on that thread.
You can run your loop on a separate thread easily by using a BackgroundWorker component on your windows form. On the event DoWork of your background worker, you place the code that has the loop that should run without block your UI. On the Form.Load event, you can start the background worker component by calling the method RunWorkerAsync. On the event handler of your button, you place a code to stop the background worker by calling CancelAsync method.
The article How to: Implement a Form That Uses a Background Operation shows exactly how to accomplish it.
About your comment on not being able to update the Text of a TextBox from a your background worker component. It happens because it is not allowed to modify the state of a windows forms control from a different thread (your background worker code is running on a separated thread) MSDN documentation says:
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
A sample of how you can update the state of your windows forms controls from your background thread will be similar to the one below (assuming that the new value is already stored on a String variable named text):
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
I borrowed this code snipped from How to: Make Thread-Safe Calls to Windows Forms Controls article. It can provide you more information about how to deal with multi-threaded windows forms.
You can use the form load event to trigger the start of the loop.
So it would handle the event Me.Load
However is it necessary for your loop to be happening inside of the UI?
This happens because your loop is keeping the window function from processing messages, such as those that tell it to repaint itself. Place a call to Application.DoEvents() inside of your loop to allow the UI to continue to function.
However, you need to ask yourself why you're looping like this in the first place. If you're, say, copying a bunch of files, this might make sense. For most tasks, though, responding to a timer tick should do the trick and won't block the UI.
You should run your loop in a background thread using the BackgroundWorker component.
Remember that the background thread cannot directly interact with the UI controls.
To report the progress on the UI, you should call the BackgroundWorker's ReportProgress method in the background thread, and handle the ProgressChanged event to update the UI.
You can call the CancelAsync method when the Button is clicked, and loop until the CancellationPending property is true.

Categories

Resources