Implementing a Progress Bar in C# - 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();
}
}
}

Related

Way to visually simulate button click in VC# on Visual Studio 2013: Please tell me what's wrong

This is my method inside a class in my Form. I'm using a custom image background on my buttons, and I want to change them so that they appear to animate:
private void restaurantButton_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(75);
restaurantButton.BackgroundImage = Properties.Resources.buttonBackClicked;
System.Threading.Thread.Sleep(150);
restaurantButton.BackgroundImage = Properties.Resources.buttonBack;
}
The animation happens if I give it a single change. The above code doesn't appear to do anything. Please help, and if you can, suggest another way to animate controls manually (i.e. control image, click time, etc.) in VC# (VS2013).
The problem is that you are blocking the UI thread and not allowing it to update. You are only using one thread here, that same thread happens to be the one that is making visual updates to your UI. So, if you put the thread to sleep that is updating your UI, it will not update (because it's asleep).
To solve this, you need to asynchronously wait on a separate thread. The async/await keyword that were introduced in .Net 4.5 will make this very easy, see the code below:
private async void restaurantButton_Click(object sender, EventArgs e)
{
await Task.Delay(75);
restaurantButton.BackgroundImage = Properties.Resources.buttonBackClicked;
await Task.Delay(150);
restaurantButton.BackgroundImage = Properties.Resources.buttonBack;
}
When you call await, what is actually happening is that the current thread is being released (freed) and Task.Delay is being executed on another thread. When that second thread is finished (waiting, in this case), it signals back to your main thread to continue.
You're sleeping on the UI thread, preventing the UI from updating.
Instead, use await Task.Delay() for a non-blocking wait.

Sleep method doesn't work properly

Ok, I have a chess board application. The application has 64 panels within a groupbox. These panels are identified with the expression CHessBoardPanels{x,y). I am using this to change the background color of the panels. I want a small delay in between where the chessPanel.BackGround changes to red and when the panels turn back to white.(approximately 1 to 2 second delay)
I have tried the Sleep function but it basically locks the application up till the task have been complete
Here is the code which I have tried:
for (int Row = 7; Row > 3; --Row)
{
chessBoardPanels[ Column ,Row].BackColor = Color.Red;
++Column;
//Add text to Moves TextBox
MovesText.AppendFormat("WhtB {0} {1}{2}", ColumnText, RowText, Environment.NewLine);
MovesTxt.Text = MovesText.ToString();
++ColumnText;
--RowText;
}
//Start White Horizonal Drill
Column = 0;
Thread.Sleep(5000); //This does not delay proerperly
for (int Row = 7; Row > 4; --Row)
{
chessBoardPanels[Column, Row].BackColor = Color.White;
++Column;
//Add text to Moves TextBox
MovesText.AppendFormat("WhtB {0} {1}{2}", ColumnText, RowText, Environment.NewLine);
MovesTxt.Text = MovesText.ToString();
++ColumnText;
--RowText;
}
I am using .Net framework 4.0 because Visual Studio 2010 doesn't support 4.5
That's what Sleep does, it pauses the thread.
What you need is to use a Timer control, and create a queue with color changes, and let the timer control call an event handler periodically, like so:
(in your Form class):
Timer timer = new Timer();
(in your Form's constructor):
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 500; // every 1/2 second
timer.Enabled = true;
timer.Start();
Then your event handler would do something like this:
void timer_Tick(object sender, EventArgs e)
{
// Read a queue, that contains timings
var nextItem = PeekAtQueue();
if ((nextItem != null) && (nextItem.WhenToChangeColor <= DateTime.Now))
{
var item = TakeFromQueue(); // as opposed to just peeking
ChangeColor(item);
}
}
What does this do? (Of course this code is not 100% complete, you'll have to add the queue-accessing methods yourself, it depends on what you want/can use)
Seeing that Sleep() freezes your application temporarily, you need a
solution that allows you to still interact with your form, while
still having a delay and doing something after that delay.
So you
need to use a timer to execute something at a later date. The timer
in this example gets executed every 1/2 second but you can use any
interval you choose.
Point is, because of this timer and this
configuration, a certain method -- the event handler -- is executed
every 1/2 second, or the interval of your choice.
So what you can
then do is, when you want a certain color change to happen later, to
queue an object that describes the color change, for instance {E5, "red", today at 04:20:30 PM}.
The event handler executed by the
timer peeks every time to see if there is something in the queue, and
to have a look at what it is. It checks whether the "moment to change
the color", as described in the queued object, is now or has already
passed. If so, it changes the color. If not, it leaves the item at
the top of the queue, and the next time the event handler gets
executed again (1/2 second later), it will perform the same check
again.
Once a color change is done, the object is taken off the
queue.
Consider using a Timer class instead. That should allow you to delay your application without locking the thread.
I have tried the Sleep function but it basically locks the application
up till the task have been complete
You need to move the logic of your application our of the main (or UI) thread. Your application will "freeze" if you call a Thread.Sleep in the main (UI) thread as the Windows event-handling loop is blocked.
Things you can use:
New Thread, Task, Background worker to execute logic of the game
Timers
Update the GUI from another thread
Sleep will lock the current thread, meaning if it's the main UI thread then no user input will be processed - and ideally you should set an hourglass cursor at that point.
To do what you want I think you need to start a background timer and then set the colours on the callback, then cancel the timer.
While the timer is ticking down the main UI will still be responsive.
You might also find that over 1 second delay will feel like too long a delay for users, but the timer period is in milliseconds so you can define a shorter delay.
Try following:
Task.Run(() => Thread.Sleep(5000)).Wait();

ToolStripProgressBar doesn't repaint when sleeping immediately afterwards

I have only a little experience with threading in WinForms. I am trying to set a progress bar to 100, sleep for 2 seconds, and then hide the progress bar.
I have:
public void ProgressBarTimerStop()
{
if (!AnyWorkersBusy())
{
if (InvokeRequired)
{
Invoke((MethodInvoker)delegate
{
ProgressBarTimer.Stop();
Thread.Sleep(1000);
Application.DoEvents();
Thread.Sleep(1000);
StatusToolStripProgressBar.Visible = false;
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Minimum;
});
}
else
{
ProgressBarTimer.Stop();
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Maximum;
Thread.Sleep(1000);
Application.DoEvents();
Thread.Sleep(1000);
StatusToolStripProgressBar.Visible = false;
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Minimum;
}
}
}
For the current problem I am experiencing InvokeRequired is false.
When I call the line:
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Maximum;
my watch window shows the value as set, but the GUI has not updated to reflect this. If I immediately sleep for a full two seconds the GUI does not update before sleeping.
If I call Application.DoEvents() without calling sleep beforehand -- it appears to do nothing.
If I sleep, then call DoEvents -- the GUI updates, and then I go back to sleep. This is correct, but I feel like my code is heinous.
Am I misunderstanding a key concept here?
EDIT: ProgressBarTimerStop is called only through "RunWorkerCompleted" event handlers.
I tried this, but it had no effect:
StatusToolStripProgressBar.GetCurrentParent().Refresh();
The 'key concept' that you are missing is that updating the progress bar's value does not immediately tell the progress bar to redraw itself.
When you update the progress bar, the GUI thread repaints the progress bar on the next window repaint. This only happens when the GUI thread becomes free to do so. Normally this happens when the window receives a paint message from the operating system. This should be picked up by Application.DoEvents, but it could be that the message isn't queued quickly enough to be picked up by DoEvents. You may be able to force it to with a Refresh call.
Call StatusToolStripProgressBar.ProgressBar.Refresh(); on every step. ProgressBar property of ToolStripProgressBar is hidden and it may not be shown in intellisense list.

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).

C# progress bar doesn't show WinForm

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.

Categories

Resources