I am attempting to add a progress bar in my c# excel add in. The progress bar appears but it does not indicate any progress until the function is finished executing.
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);
backgroundWorker1.ReportProgress(i); //run in back thread
}
}
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) //call back method
{
progressBar1.Value = e.ProgressPercentage;
}
public void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) //call back method
{
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;
}
How should I change this so that the progress bar will not stop working during the execution of FUNCTIONTORUN()? The progress bar is initialized using the Visual Studio designer. The name of the progressbar object in the form class is progressbar1.
Thank YOU!
The DoWork event for your progress bar should be (or contain) FUNCTIONTORUN(). FUNCTIONTORUN() should use the ReportProgress method.
What does FUNCTIONTORUN do? It is very likely blocking the UI thread, causing your updates to the progress bar to not be seen until it returns. More than likely, FUNCTIONTORUN needs to run in a background worker, and have it periodically report progress, and that's where you'd update the progress bar.
I would like to expand on the following statement. Matt Greer can correct me if I am wrong.
What does FUNCTIONTORUN do? It is very likely blocking the UI thread,
causing your updates to the progress bar to not be seen until it
returns. More than likely, FUNCTIONTORUN needs to run in a background
worker, and have it periodically report progress, and that's where
you'd update the progress bar.
What I believe he means by this is you need to update progressBar1.Value within
FUNCTIONTORUN(); you can still use a similar method.
Related
I have an BackgroundWorker:
BackgroundWorker worker;
private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
DoWork Event
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
percentFinished++;
worker.ReportProgress(percentFinished);
//here I start my operation
//operation....
//operation end
}
e.Result = percentFinished;
}
Progresschanged
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Completed
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Asynchroner Thread kam bis zum Wert:
"+e.Result.ToString());
btnStartEnd.Text = "Starten";
}
And finally my button:
private void btnStartEnd_Click(object sender, EventArgs e)
{
if (worker.IsBusy)
{
worker.CancelAsync();
btnStartEnd.Text = "Starten";
}
else
{
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum;
}
worker.RunWorkerAsync(progressBar1.Value);
btnStartEnd.Text = "Stoppen";
}
}
This code works but I get a loop for my operations until the percentage is 100, so the operation starts 100 times and so takes 100 times longer.
The goal should be that the operation only starts one time and the percentage counts from 1-100.
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
Normally you won’t add the loop inside the DoWork method
If you want to load for example 100 files from the file system and report the progress it could look like that:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for(int i = 0; i < 100; i++)
{
// Load file and do something with the content
...
// Report the progress which causes the ProgressChanged event to be fired
// And update progressbar with the UI thread
worker.ReportProgress(i);
}
}
If you only have one long running operation that needs to be executed inside the DoWork method it needs to be executed asynchronously
Here is one example how you could call an action asynchronously in .NET:
Action action = () =>
{
for(int i = 0; i <100; i++)
{
Console.WriteLine(String.Format("Step {0} of long running operation", i));
Thread.Sleep(1000);
}
};
var r = action.BeginInvoke(null, null);
while(!r.IsCompleted)
{
Console.WriteLine("Waiting...");
Thread.Sleep(100);
}
However in .NET there are many more ways to do it. See for example:
http://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx for
Async patterns in .NET
http://msdn.microsoft.com/de-de/library/hh191443.aspx for Async
programming with await/async (new in .NET 4.5)
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
The BackgroundWorker class doesn't know anything about your operations and about its progress. It's your job to determine when to report the progress.
In general the workflow for a background worker looks like this:
UI thread calls RunWorkerAsync.
DoWork event handler is called on a different thread. During the event handler you can report progress using the ReportProgress method
If you report a progress then the ProgressChanged event handler is called on the UI thread. Here you can update a progress bar for example.
When your event handler for the DoWork event exits the RunWorkerComplete event is raised.
Now why does every example for the BackgroundWorker has a for-loop? Because it's very easy to write, and measuring progress for a for-loop is trivial. Unfortunately this quite often isn't useful for different kind of operations.
If your long running operation processes N files then it's pretty obvious that you can update the progress bar after every item by 1/N. That's what the for-loop example does.
But if you only have one long running operation then you simply don't have any chance to get the progress unless the operation itself supports reporting it or if you can somehow estimate the progress.
The BackgroundWorker can't magically give a long running operation a progress bar. It only enables you to run the operation in the background.
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.
I have question about progressbar show value.
I have this main thread
private void button1_Click(object sender, EventArgs e)
{
progress prog = new progress();
progress.progressEvent += new progress.progressEventHandler(progressEvent);
for(int i=0;i<100;i++)
{
Thread.Sleep(100);
prog.incA();
}
}
void progressEvent(object sender)
{
if (progressBar1.InvokeRequired)
{
//Tady mi to caka az kym nedobehne cyklus for a pak zacne tohleto fungovat
progressBar1.Invoke(new ChangeProgressBarValue(ProgressStep));
}
else
{
ProgressStep();
}
}
public void ProgressStep()
{
progressBar1.PerformStep();
}
public class progress
{
private ThreadStart ts;
private Thread th;
private bool status = true;
public delegate void progressEventHandler(object sender);
public static event progressEventHandler progressEvent;
private int b,a = 0;
public progress()
{
ts=new ThreadStart(go);
th = new Thread(ts);
th.IsBackground = true;
th.Start();
}
public void incA()
{
a++;
if(a==100)
status = false;
}
private void go()
{
while (status)
{
if (a != b)
{
b = a;
if (progressEvent != null)
progressEvent(this);
}
}
th.Abort();
}
}
and my problem is IF start main thread and call IncA this method call event and in event is progressbar invoke
and this invoke waiting to end main thread FOR
why waiting?
thx
Your loop in the main thread is preventing "paint" events from happening. Since you are calling your progress bar's function from withing that thread, you will never see the updates.
You need to move the code to do the incrementing to another thread entirely.
Here is a sample of what you want to do using a Button, a BackgroundWorker, and a ProgressBar:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Hope this helps!
The progress bar control is a UI object, and is created on the UI thread. When you call Invoke or BeginInvoke to update it, you are asking the UI thread to do the updating.
However, the UI thread is busy - in your button CLick event handler, you have a loop which Sleep()s the thread and calls prog.IncA in a loop. So it never exits back to the main UI loop (which is what dispatches windows messages and updates the UI). Your progress bar is being updated internally, but it never gets a chance to redraw because the UI thread is "busy".
The "processing" code (that is looping and calling prog.IncA()) should not be running on the UI thread at all - you need to start it off in a separate thread and then exit your Click handler so that the UI can continue to update.
Note that this has a side effect - if your UI thread is running, then the user will be able to continue interacting with your program, and so they can click again on the button and kick off another background thread - so you have to be very careful to make sure that the user can't do anything "dangerous" in the UI while you are busy processing.
I suggest you look at some introduction-to-threading tutorials to get an idea of how to use BackgroundWorker or another mechanism for running code in a separate thread. Once you understand that, you can add a progress bar. (And note that although a progress bar sounds like the simplest thing to do, it is actually rather a difficult thing to do due to the need for the UI thread to continue running but not let the user do anything dangerous during your processing)
In my program [C# + winforms]. I have progress bar & listview.
Through one method i am performing some operations & then updating data in Listview. The no of records added is the value i am setting for ProgressBar.value property. What i want here is, According to value of progress bar, it should show its progress. However the progress bar is not getting updated. Only at the end of method execution progress bar shows entire progress i.e. 100 %
Can someone help me in this regard?
Thanks,
Amit
It sounds like you are blocking the UI thread - i.e. you haven't released the system to do any painting.
A hacky answer is to inject Application.DoEvents() into your code - but this is risky, and has problems with re-entrancy etc; and it is just a bit hacky.
A better option may be to do the processing on a BackgroundWorker, periodically switching to the UI thread to update things (Control.Invoke) - but this may be tricky if you are adding lots of items to a ListView.
Full example (although you might want to batch the UI updates - not a row at a time):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class MyForm : Form
{
BackgroundWorker worker;
ListView list;
Button btn;
ProgressBar bar;
public MyForm()
{
Text = "Loader";
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
list = new ListView();
list.Dock = DockStyle.Fill;
Controls.Add(list);
btn = new Button();
btn.Text = "Load";
btn.Dock = DockStyle.Bottom;
Controls.Add(btn);
btn.Click += btn_Click;
bar = new ProgressBar();
bar.Dock = DockStyle.Top;
Controls.Add(bar);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn.Enabled = true;
}
void btn_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
btn.Enabled = false;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
string newRow = "Row " + i.ToString();
worker.ReportProgress(i, newRow);
Thread.Sleep(100);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
list.Items.Add((string)e.UserState);
bar.Value = e.ProgressPercentage;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
}
Really Sorry Friends,
Actually, I was assiging value to ProgressBar.value field but didnt use update() method. I used that & my problem got resolved.
Thanks all for your replies
As Marc said, you want to make sure that you spin off a new thread to do your long running computation. That way the User Interface thread (which is the one that has to do all the screen updates) can redraw the progres bar whenever you change the percent complete.
It's important to note that only the UI thread can update the interface. So, once you are running on a separate thread, you have to go through an extra hoop to make sure that your UI change is processed on the UI thread. If you aren't sure what thread you are running on, you can check the value of InvokeRequired (if your class is a System.Windows.Form) to see if you are actualy in the UI thread.
To get your command processed on the UI thread, use the Control.Invoke() function to make sure the update is processed on the UI thread for the control you are working with.
In my sample code below I'm creating a delegate function type and declaring the invoked function in advance....I've not done it with any of the cool C# 3.5 functions, but I bet you could work up a lamba expression to do the same thing.
private void bCreateInvoices_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(CreateInvoices);
worker.RunWorkerAsync(this);
}
// Here is the long running function that needs to update the progress bar
public void CreateInvoices(object sernder, DoWorkEventArgs e)
{
int totalChecked = CountCheckedServiceOrders();
int totalCompleted = 0;
foreach (...data to process...) {
totalCompleted++;
if (InvokeRequired) {
Invoke(new Change(OnChange), "status text",
totalCompleted, totalChecked);
}
}
}
// this code updates the status while a background thread works
private delegate void Change(string status, int complete, int total);
private void OnChange(string status, int complete, int total)
{
if (status == null) {
progressBar.Visible = false;
lStatus.Text = "Task complete";
progressBar.Value = 0;
} else {
progressBar.Visible = true;
progressBar.Minimum = 0;
progressBar.Maximum = total;
progressBar.Value = complete;
lStatus.Text = status;
}
}
Take a look at the MSDN Control.InvokeRequired manual page and the MSDN Control.Invoke manual page for some more info.
The ProgressBar.Value must be between 0 and 100.
My guess is that your problem is that you're updating the ListView on the GUI thread. That means you'll need to call Application.DoEvents() after changing the ProgressBar.Value property.
It would be best to run on a BackgroundWorker and use the ProgressChanged event to handle the ProgressBar update.
Here's another question about the same topic.