i have a program(winform) that runs through a lot of files(in the area of 400 some times) in a folder and to show its progressing i use a progress bar. the info get posted into a listview if that makes a difference. The problem i am having is that when the pc is slow, im working with folders over a network or if it is a really big amount of files the ProgressBar just stops updating. if n leave the program it will continue and finish, but the progress bar gets stuck at some stage. And then only shows that it is complete (additional note: i have noticed that isnt just the progress bar. sometimes it is the whole form. but again, when the program is done it is fine. and while it is working you can move the form, just not interact with it)
Now here is where it gets tricky. I know it can be solved by using threads. i have however been told not to use it(let us not go into the why, lets just except it, whether it is stupid or not). i have also tried to refresh the form, and to refresh the bar itself. none of it seems to work. it isn't a good idea to use it, but even tried .DoEvent, it also doesn't work
How can i get the progress bar to update? any ideas?
here is some of the code i use for the progress bar
//before the loop
progress = iCount1;
progressvalue = 0;
double increment = 100 / (double)progress;
//this is at the end of the loop
progressvalue = progressvalue + increment;
then there is a bit of code. some of it to stop the value from going out of bound. the rest not related to the progress bar at all
edit: o, i have that 1 variable. and it basically runs over 3 loops. so that we have one continuous bar. not the bar running 3 times
edit: it seems to work fine for folder on my pc. but when it is run on a slow pc or over a network, that is when it does this
EDIT:Im getting answers about backgroundworker and threads. so is there no way to do it without threads?
Update: i finally convinced my boss to do it with backgroundworker. so ill be doing it in that. but for this question i guess the .DoEvent answer is the best
When the form-thread is busy, it will be very unresponsive and not draw the form at all. Even if you dont want threading i think you ought to reconsider..
I would use a backgroundworker as in this example: here it is a quite simple setup.
You could add an Application.DoEvents() then.
For example:
while(!end)
{
//You processing logic here
Application.DoEvents();
}
Note that it is generally discouraged since it will break the normal flow of events and may have unpredicted results.
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx
http://www.codinghorror.com/blog/2005/08/is-doevents-evil-revisited.html
Related
I want to run long lasting methods, even containing time consuming dlls, while a Spinner GIF is rotating.
OK, the most accepted solution is to run these methods in a BackgroundWorker while the GIF is shown in the main thread and I have already done, successfully, it but… but I am still curious to know if it is really impossible to do do the other way round !
This would simplify the calls to those methods, especially if they have arguments and return values, avoid the use of Invoke if they contain Labels to show the working progress, etc…
I have spent quite a lot of time browsing the Web but all the suggested solutions don’t work for me: as soon as the program calls my methods the Spinner stops rotating and resume working only when the methods end.
Ciao and thanks for any suggestion.
DONE!!
Thanks to the Camilo Terevinto sentence “You cannot do UI work on a non-UI thread” I asked myself if it was not possible to create the PictureBox that holds my spinning GIF in another thread and…
I created a new borderless Form (named frmSpinner) with inside a PictureBox running the Spinner.gif.
Then I used, in the main Form with the long running methods, a BackgroundWorker that, in the DoWork event, has a frmSpinner.Show().
Now the Spinner rotates endless without interrupting and resuming.
I have still to solve the problem how to place the Spinner Form on the right position on the main Form but, with the help of the PointToScreen method, it shouldn’t be to difficult.
Ciao and thanks for the suggestion.
Dealing explicitly with threads in a WPF/c# project is not canonical, unless you have something very specific to do, using Tasks is the modern way.
You can have a background task, started with for instance:
Task.Run(() =>
{
// blocking methods running outside UI thread
var newPropValue = //... update a UI property
Application.Current.Dispatcher.Invoke(() =>
{
//Running in UI thread.
PropValue = newPropValue;
});
});
With this pattern you can remove all the heavy/blocking work from the UI thread and have a flowing UI experience.
I have my main form kicking off some background work using Delegate.BeginInvoke and within those delegates I am adding some rows to be displayed on a DataGridView on my main form. I have a backing dataset and a BindingSource attached to that, which I use as the source for my DataGridView.
Whenever I add a row, I do this:
ResultsDataTable.AddResultsRow(row);
RefreshDataGridView();
Where RefreshDataGridView() looks like this:
private void RefreshDataGridView()
{
if(InvokeRequired)
{
//I have tried dgvResults.Invoke() as well
dgvResults.BeginInvoke(new Action(() => RefreshDataGridView()));
}
else
{
dgvResults.Refresh(); //this is where it hangs
dgvResults.FirstDisplayedScrollingRowIndex = dgvResults.Rows.Count - 1;
}
}
It works well, when I add a new row it displays instantly and scrolls (despite my scrollbar not being drawn correctly but I can live with that) as expected, but only when I run the app through the debugger. When I start it without debugging, the application hangs whenever a row is added and it actually needs to scroll.
I've built the application in debug mode and run it without debugging, then let it get to the point where it hangs and attached the debugger to the process to see where it is happening (see comment in code above).
I know this is happening because my main thread is waiting for something but I have no clue what it is waiting for or how to find out.
Does anyone have any ideas?
Update: I started it without debugging then attached the debugger again, and found that the main thread is getting stuck updating a control, but I can't figure out which one.
Update 2: I got rid of the refresh and now it doesn't hang when adding the new row, but I can't resize my form at all without it hanging.
Update 3: It seemed to be hanging while trying to update the scrollbars of the data grid, so I encapsulated it in a panel and gave that scrollbars instead. With a bit of hacking to get the data grid to dynamically size itself based on the data it contains, it's a bit glitchy but no more deadlocks.
I had the same issue. You mentioned that your DataGridView has a bindingsource attached to it. If you are doing something to your source, then you are affecting the DataGridView. You will need to place that line of code that is modifying the source inside the BeginInvoke statement. Once I did that, the issue is gone.
Here is what my code looks like:
private void exportToExcelButton_Click(object sender, EventArgs e)
{
txtBox.Clear();
txtBox.AppendText("Beginning Export...");
ExportExcel(txtBox);
txtBox.AppendText("Export complete...");
}
The problem I am having is that whenever the button is clicked (to execute the function above), only part of the current text in the TextBox (System.Windows.Forms.TextBox) is cleared, and replaced with the first line: "Beginning Export ...".
However once the function ExportExcel(txtBox) is done executing, then the entire text is replaced by the new one generated in ExportExcel(txtBox).
Inside ExportExcel(txtBox); I have several txtBox.AppendText() statements explaining to the user the actions being made.
I have tried clearing the text with txtBox.Text = String.Empty; and txtBox.Text = "";and neither have worked.
Let me know if anything needs to be clarified, thanks.
Looks like you're blocking the GUI thread, preventing the text box from redrawing itself. This is one reason why you shouldn't perform long-running tasks on the GUI thread. Use a background thread instead. That way you leave the GUI thread free to perform important operations like drawing, responding to mouse clicks, etc.
Have you tried the textBox.Refresh , before calling txtBox.AppendText("Beginning Export...").
The method invalidates the control.
On the other hand, if you use a background thread, then you should update the UI only by overriding the Progress Changed event. Background threads are not meant for updating user interfaces. Try searching for Worker threads and UI threads. They correlate to MFC, but the concept is the same.
Also keep in mind the cross thread calls.
I agree with dvnrrs. However if you are unable to do this, try calling txtBox.Refresh();after adding each line of text.
There is another method called Application.DoEvents(); that has a similar behavior, but its use is not recommended since it sort of short-circuits the normal application flow and can cause your application to fail unexpectedly or do strange things.
I have a program that runs a series of methods in other threads within one window and let's the user know what's going on using a status bar. The status bar updates are in the main thread which set's the status bar and then refreshes the GUI. There are several blocks of code in series each looking something like this:
Thread do1Thread = new Thread(Class.Method);
do1Thread.Start();
// inform user
this.status.Text = "Doing stuff 1...";
// update GUI
Utility.RefreshGUI();
// join thread
do1Thread.Join();
Sometimes the status bar does indeed update but often is stays on the first status until the end when it displays the last status. Occasionally is sticks on "Ready." which is the default.
Note that two of the blocks take a few seconds so there should be time for it to update. Also, the program is written in C# (Mono) using GTK# for the GUI.
How can I ensure that that the GUI updates to reflect the change?
The problem is that the Join() call blocks the UI thread which blocks all window messages.
Can you use a BackgroundWorker and execute whatever code you have after the Join in the RunWorkerCompleted call?
You need to dispatch Update message to UI thread, call invoke instead of direct property
this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen"));
The best way I have found to update a control in a primary thread is to set a delegate for updating and invoke that from other threads.
You have to use observe and observable pattern.
EDITED:
It is really better divide logic and view parts of code.
Here is an example in real world how to use. Pattern
Could you check whether you are using a StatusStrip control?
If so, your code looks like setting directly the Text of Stautus Strip Control
this.status.Text = "Doing stuff 1...";
So it wont reflect in the Status Strip as Text. You have to place a toolstriplabel and need to set its text in this case.
Please check the post here
My software requires to store the directory and registry structure for some particular locations. This usually takes a long time. Let's say I have a method called SaveState() which does that.
I want to wrap this into another method, SaveStateWithProgress(), so that when I call it, a modal dialog appears, which shows a simple progress bar and has a button to cancel the operation. The way I see it I may need to use two threads, although in VB6 or Java I used to get by with the equivalent of Thread.Yield() command - not sure if it is the best practice, and even if there is something similar in C#. What is the best way to go about it?
The best method in C# is use a BackgroundWorker and run your intensive operation inside that background worker.
Here is a tutorial that includes instructions on how to cancel your operation half way.
Here's a site that I think would satisfy what you need.
It has example of using a progress bar and background worker (using BackgroundWorker.RunWorkerAsync()).
In C#, you can call Thread.Sleep(0) or Thread.Sleep(1) to do the same thing as Java's Thread.Yield().
With that said, you will definately need to do your SaveState() in a separate thread. Have a variable in your SaveState() function that gets updated as SaveState() progresses. Put a timer control in your modal progress form and have the timer check this variable that SaveState() is updating. Update your progress bar appropriately
ProgressBar p = new ProgressBar();
p.Location = new Point(10, 10);
p.Size = new Size(100, 30);
p.MarqueeAnimationSpeed = 30;
p.Style = ProgressBarStyle.Marquee;