If you can picture this as a loading bar:
for (int i = 0; i < r.Width / 2; i++) //Fills half the loading bar.
{
Rectangle rect = can.Children[3] as Rectangle; //getting the 3rd element of my Canvas
if (rect != null)
{
rect.Fill = brush;
rect.Width = i;
Thread.Sleep(50);
}
}
So, the application will not show the progress of adding additional rectangles. Instead it just freezes up for the total time of the thread sleep and when the for loop is finished it will show the entire bar.
Why is this?
I'm aware there is a loading bar built in, but this is just an example of problems I have been having.
I'd recommend you start reading about Threads, background workers, dispatchers. Unless you add threads to your solution, the entire applications runs with the help of UI thread.
If you have got task which can be performed serially, you need not implement Threads or background worker. In your case, you are trying to do 2 task.
Update the rectangle and draw it on window
Sleep thread for 50 milliseconds
When UI thread executes thread.Sleep(50); it cannot draw (show progress in rectangle). That's the reason your application kind of freezes for seconds and gets completed before you could see any progress. I am going to try and explain this with 2 examples.
Example:1
Add a progress bar with IsIndeterminate="True" and Button1 to your demo application.
<ProgressBar IsIndeterminate="True" Height="20" Name="progressBar1" Width="177" Visibility="Hidden" />
In code behind,
private void button1_Click(object sender, RoutedEventArgs e)
{
progressBar1.Visibility = Visibility.Visible;
Thread.Sleep(5000);
progressBar1.Visibility = Visibility.Hidden;
}
If you run this code, You will not see any progress bar as the code is getting executed serially. To make sure that progressbar is getting shown and hide properly, do following changes.
Example 2:
private void button1_Click(object sender, RoutedEventArgs e)
{
progressBar1.Visibility = Visibility.Visible;
Thread t = new Thread(DoMyWork);
t.Start();
}
private void DoMyWork()
{
Thread.Sleep(5000);
//hide the progress bar after completing your work
this.Dispatcher.Invoke((Action)delegate { progressBar1.Visibility = Visibility.Hidden; });
}
Run this code and you can see that while UI thread updates progressbar, new thread created executes DoMyWork function.
You will need this.Dispatcher.Invoke((Action)delegate as progressBar1 was created in UI thread and you cannot access it in a another thread. i.e.Thread t = new Thread(..)
P.S.- This is dirty of way to coding. It is just to give an idea that how things should work in OP's case.
Related
Please could someone suggest why the following doesn't work? All I want to do is display an indeterminate progress bar which starts when the button is clicked, then after I've done some work I set the indeterminate progress bar to false to stop it.
However when I run the code below the indeterminate progress bar doesn't even start. I tried commenting out the line this.progressBar.IsIndeterminate = false and when I do this the progress bar does start but then doesn't terminate.
private void GenerateCSV_Click(object sender, RoutedEventArgs e)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate ()
{
this.progressBar.IsIndeterminate = true;
// do some work
Thread.Sleep(10 * 1000);
this.progressBar.IsIndeterminate = false;
}));
}
Your code can't work because the "do some work" is happening on the same Thread on which the UI works. So, if that Thread is busy with the "work", how can it handle the UI animation for the ProgressBar at the same time?
You have to put the "work" on another Thread, so the UI Thread is free and can do its job with the ProgressBar (or other UI controls).
1) create a method that does the work and returns a Task, so it can be "awaited" for completion:
private async Task DoWorkAsync()
{
await Task.Run(() =>
{
//do some work HERE
Thread.Sleep(2000);
});
}
2) Put the async modifier on the GenerateCSV_Click:
private async void GenerateCSV_Click(object sender, RoutedEventArgs e)
3) throw away all that "Dispatcher" / "Invoke" etc stuffs, and use the DoWorkAsync this way:
private async void Button_Click(object sender, RoutedEventArgs e)
{
PB.IsIndeterminate = true;
await DoWorkAsync();
PB.IsIndeterminate = false;
}
So, what's happening now? When GenerateCSV_Click encounters the first await... it begins to work automatically on another Thread, leaving the UI Thread free to operate and animate the ProgressBar.
When the work is finished, the control of the method returns to the UI Thread that sets the IsIndeterminate to false.
Here you can find the MSDN tutorial on async programming:
https://msdn.microsoft.com/en-us/library/mt674882.aspx
If you Google "C# async await", you'll find dozens of tutorials, examples and explanations ;)
I have a very simple form with a progress bar on it and a delete button. When the user clicks "Delete", a stored procedure runs for around five minutes.
I would like a simple marquee progress bar to run but it never appears. I read all the other questions about this but they all required a background worker.
Is it possible to make a marquee progress bar without a background worker?
public partial class ProgressBarSample
{
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
}
}
Your code
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
prevents windows messages from being processed by your application until the delay completes because you block the thread responsible for processing those messages. Windows relies on those messages being processed in a timely manner.
The result is that your user interface will appear unresponsive to the user.
There are a number of mechanisms that allow you to do your processing on a second thread. BackgroundWorker is one that was commonly used at the time WinForms first came out and still a solid choice. You can use any technique that does the long-running work on another thread, but you must do it on that other thread.
You can use the async pattern to simplify the coding for that other thread
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
await Task.Run(() =>
{
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
});
progressBar1.Visible = false;
}
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces your problem, it's impossible to say for sure what the issue is. However, the code you posted won't work. Your btnDelete_Click() method sets the Visible property to true, but then immediately sets it back to false, because the Task.Delay() method doesn't actually block.
Probably what you want is this:
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
await Task.Delay(10000);
progressBar1.Visible = false;
}
BackgroundWorker is the generally accepted method for doing such background work, hence the name; but for a primitive "show for X period of time", you could use a Timer that checks the amount of time passed since (in this case) delete was last clicked to see if it should hide the control (and disable itself, no use ticking when there is nothing to do.)
Something like:
public partial class ProgressBarSample
{
TimeSpan pbShowDuration = [blah blah];
DateTime pbShowFrom = DateTime.MinDate;
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
pbShowFrom = DateTime.Now;
timer1.Enabled = true;
// run stored procedure that takes around 5 minutes
}
private void timer1_Tick(object sender, EventArgs e)
{
if ((DateTime.Now - pbShowFrom) > pbShowDuration)
{
timer1.Enabled = false;
progressBar1.Visible = false;
}
}
}
But how are you planning to update the progress bar?
With the new task, sync, await features in C# you have a lot of options. If you don't need to do anything other than let the user know the operation is done you can start your progress bar, then start a task that runs your proc and when it's done stop the progress bar. I would personally put something other than a progress bar. To me a progress bar means you have a finite amount of time to wait. If your SP can vary in time I would go with some kind of busy display icon or something like that.
I personally would useh Task.ContinueWith in this case.
This MSDN article show a great way to handle it.
https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx
Background worker is an old method which is superseded by using Tasks. Tasks have more functionality, can do things backgroundworkers cannot, and are much simpler to use.
I have been developing a windows forms application in vc# express 2013. I have a statusstrip control where i have a label and a progress bar. I want to show the progress of progressbar in label but it is not updating with the progress bar value. it only gets updated when the progress bar is complete i.e. when value is 100 and the job is finished. The strange thing is that the progress bar is updated but the label is not... Any ideas what might be happening here? By the way, I raise a progress event in my own code which is handled and the progress is updated accordingly...
Thanks
I raise the event ProgressTick in MGFDataHandler Class
if ((hundredthValue*progressMultiplier == lineCounter) || (lineCounter==totalLines))
{
//Raise the progress bar event!
EventHandler handler = ProgressTick;
if (handler != null)
{
handler(this, e);
}
progressMultiplier++;
}
I then handle it in windowsform as follows:
void msDataHandler_ProgressTick(object sender, EventArgs e)
{
var progress = (int)((MGFDataHandler)sender).progressMultiplier;
if (progress < 100)
{
this.progressStatusString.Text = "Progress: " + progress + "%";
this.progressBar.Visible = true;
this.progressBar.Value = progress;
}
else
{
this.progressStatusString.Text = "Done";
this.filterMSDataToolStripMenuItem.Enabled = true;
}
}
The function which starts the whole thing is in the windowsform_activated event and MGFDataHandler object is an attribute of the windowsform.
private void GlycoMSPipelineMainFrame_Activated(object sender, EventArgs e)
{
switch (this.whoActivatedMe)
{
case WhoActivatedMe.MSFilterWizard:
{
this.whoActivatedMe = WhoActivatedMe.Me;
foreach (String currentmgffilename in this.currentMGFFileNames)
{
mgfDataHandler.Filter_MS_Data(currentmgffilename);
}
this.populateTreeViews();
break;
}
default:
{
break;
}
}
}
It is the Filter_MS_Data that raises the event.
Try using a background worker (found in System.componentModel) to run the task that you are performing. When updating the label or progress back you will need to check if invoke is required.
Something like this.
If (label.InvokeRequired) {
label.Invoke(new MethodInvoker(delegate
{
label.Text = progress.Value;
});
}
Invoke is required to prevent cross threading operations which in turn can produce race conditions.
Forgive the formatting i typed this out on my phone.
You will need to perform the population using a background worker. I think this will resolve your issue.
Edit:
The background worker provides a do work event which will run on another thread. It also provides a finished event so you know when the task is complete.
I think what is happening here is that your program is performing work on the ui thread which prevents the ui from fully updating.
I do need some more information for a full answer, but the direction in which this is heading to is quite clear to me.
The problem is that you're probably doing everything in the same thread. When you have a big job you need to use a background thread to do the work, and let the main thread (i.e. a UI thread) handle the UI.
This is a big topic and I'm just scratching the surface here, so first of all read the following links and try to create a thread for your job and invoke the update of the progress bar yourself. If not successful, then post a new question with more information.
The links you should read are:
Threads in C#
ProgressBar in C#
I realize this is a very old question, but i found it with the same issue in VS2019. I found that rather than adding a thread, you can call the Refresh method of the container of the label and ProgressBar.
void msDataHandler_ProgressTick(object sender, EventArgs e)
{
var progress = (int)((MGFDataHandler)sender).progressMultiplier;
if (progress < 100)
{
this.progressStatusString.Text = "Progress: " + progress + "%";
this.progressBar.Visible = true;
this.progressBar.Value = progress;
}
else
{
this.progressStatusString.Text = "Done";
this.filterMSDataToolStripMenuItem.Enabled = true;
}
this.progressStatusString.Owner.Refresh();
}
I have a c# form app that serves as an UI and executes an external exe. I want to make a progress bar increment until the external exe finishes executing. so i have the following code:
// create thread and Start external process
Thread MyNewThread = new Thread(new ThreadStart(startmodule));
MyNewThread.Start();
do
{
if (progressBar1.Value < 100)
{
progressBar1.Value++;
}
} while (MyNewThread.IsAlive);
label5.Text = "Status: Done";
// startmodule()
void startmodule()
{
ProcessObj = new Process();
ProcessObj.StartInfo.FileName = ApplicationPath;
ProcessObj.StartInfo.Arguments = ApplicationArguments;
ProcessObj.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
ProcessObj.Start();
}
Instead it fills the bar up instantly and shows "Done" message but the external exe (AppPath) still runs in the background.
Please post some ideas im stuck. i don't know whats wrong. Thank you for your time.
You cannot make this work, you cannot guess how long the process will take. Set the ProgressBar.Style property to Marquee. Set it Visible property to true when you start the process. Use the Process.Exited event to set it back to false. Like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void ButtonRunProcess_Click(object sender, EventArgs e) {
var ProcessObj = new Process();
ProcessObj.SynchronizingObject = this;
ProcessObj.EnableRaisingEvents = true;
ProcessObj.Exited += new EventHandler(ProcessObj_Exited);
ProcessObj.StartInfo.FileName = #"c:\windows\notepad.exe";
// etc...
ProcessObj.Start();
progressBar1.Visible = true;
}
void ProcessObj_Exited(object sender, EventArgs e) {
progressBar1.Visible = false;
}
}
Well the loop is being run so fast, that it reaches 100% before your task is actually completed. The condition that the loop is being check for (The thread being alive) is going to be true until your task is completed, but the loop is causing the progress bar to fill up prematurely.
In order to run a progress bar you have to be able to quantify the progress of the long running task. You have nothing in the code that attempts to quantify this.
You would need there to be communication between the two processes in order to make this progress bar work well. In other words the external process needs to send messages back to the parent app informing the parent app of the measure of progress. Now, that can be hard to achieve so a marquee style progress bar may be more appropriate.
Finally i got some "free" time to test the backgroundworker as suggested above. i can say it's the best solution and it doesn't freeze the UI. Example implementation follows:
preparemodule()
{
ProcessObj = new Process();
ProcessObj.StartInfo.FileName = ApplicationPath;
ProcessObj.StartInfo.Arguments = ApplicationArguments;
}
void run_Click(object sender, EventArgs e)
{
preparemodule();
backgroundWorker1.RunWorkerAsync(ProcessObj);
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int i=0;
ProcessObj.Start();
while (checkifexists("notepad", 0) == true)
{
i++;
label5.Text = "Status: notepad running... " + progressBar1.Value.ToString() + "%";
Thread.Sleep(3000);
backgroundWorker1.ReportProgress(i);
if ((backgroundWorker1.CancellationPending == true))
{
e.Cancel = true;
}
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage <= 100)
{
progressBar1.Value = e.ProgressPercentage;
}
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label5.Text = "Status: Done";
}
void cancel_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
As you see we can even cancel it. and by checking if notepad is running we can increment out progressbar. Dont forget to enable bgWorker's "reportsprogress" and "supportscancellation" properties somewhere in your code. i hope it helps someone.
First, #Tim answer is right about what is happening.
If you can control the external app, make a way to it communicate with the main process telling the current state and update the progress bar according to these messages.
If is not possible, try to estimate the execution time and set the progress according to the execution time. This is valid if it performs always in same time for the same task.
Background worker thread was designed for this sort of thing.
It has an event you can fire while processing something, you handle it and update your progress bar. Course as noted by others you don't seem to have any measure of progress, just some time has passed, so it's not really an indication of progress you want but some sort of "I'm busy" animation, if you use a progress bar for that you get all sorts of issues that drive the UI boys mad, like it never gets to 100%, or it gets to 100% well before the operation has finished, or even cycles round.
So if you can indicate some progress from the thread, e.g if you are looping through X items fire the progress event every 10% of X. Use a Background worker thread.
If you can't don't use a progress bar kick the thread off an make some animated control visible. When the thread finishes make the animation invisible again. What and how of the animation is up to you and your UI boys.
Currently my application is doing encryption of files and folder and i trying to have a progress bar to make the application a nicer interface and also to have know how long it take to encrypt.
However this is my first time using progress bar and i kinda confuse with all the terms mention such as background worker,steps,maximum etc. was wondering anyone know how to create and set a simple version of progress bar. thanks in advance .
AFTER see-ing swordfish suggestion and trying it out..here the code i have
this is the part of my code based on the link provided and i tried it
and the part where i used part of the code to the button
public LockPasswordBox(IFile[] info)
{
InitializeComponent();
ifile = info;
// 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);
}
// 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;
}
button code
private void lockButton_Click(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
Problem facing is..after i press the button.it just straight pop out the message box encryption success.
If you have Minimun = 1, Maximum = 100 and Step = 1 you need to call PerformStep() 99 times for the progressbar to complete.
If you can not get accurate information from lControl.Encrypt(details) about how far the operation is gone you can not know when to update all those steps on your progressbar.
The MSDN Documentation has a simple example on how to get the progressbar moving.
i had a similar requirement and this helped me with my task. Hope it will help you too.
http://www.codeproject.com/Tips/83317/BackgroundWorker-and-ProgressBar-demo
Its quiet simple actually all you have to do is report the progress from the background worker using the not so mysterious reportprogress method and have a method to update the progress bar when ever the progress is reported.
Try to implement it this way and if you hit a road block post your code.