Progress Bar with Label — Unable to Update Label in ProgressChanged - c#

I have a background worker with a long running task. The task goes through a list of files and I want to update the user with which file we are on. I have a tool strip that has a label named panel1.text. The progress bar is working however the label is not changing in my ProgressChanged method i.e. It should say Processing File1 then change to Processing File2, but it stays on the default of Processing.
private void btnProcess_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Maximum = 1000000000;
panel1.Text = "Processing "; // this appears properly
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(processFiles);
worker.ProgressChanged += ProgressChanged;
worker.RunWorkerAsync();
while (worker.IsBusy)
{
// the reason for this is because nothing can happen until the processing is done
toolStripProgressBar1.Increment(1);
}
// more processing
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
panel1.Text = "Processing "+ e.UserState.ToString(); <<<---- This is Not Updating panel1.Text but it evaluates properly
}
private void processFiles(object sender, EventArgs e)
{
int retVal = 0;
foreach (string fileName in listBox1.Items)
{
ProgressChangedEventArgs ea = new ProgressChangedEventArgs(1,fileName);
ProgressChanged(this, ea);
// do more processing
}
}
I would appreciate any help.

You are using the same thread, which is being blocked by another process. You need to use a Task to create a new thread and possibly use Dispatcher.BeginIvoke if the control is on the other thread. Make sure whatever Button Click, etc is happening is marked with the Async keyword as well to make it Asynchronous.
Example:
Await Task mytask = Task.Run(() =>
for(var i = 0; i < 1000; i++)
{
Label.Dispatcher.BeginInvoke( () =>
UpdateMe(int i, LabelClass/Component class/component)});
Then inside the Label Class or wherever the label is:
Public void UpdateMe(int i, LabelClass class)
{
class.label.content = Cint((i/Total)*100);
Thread.Sleep(500);
}
There are other ways to do it as well such as Binding the value to the UI, but this will give you a better understanding of why its not working and how things work with other threads.
If you want to really get a visual understanding call:
`Console.WriteLine($"Current Thread ID: System.Threading.Thread.CurrentThread.ManagedThreadId}");`
Right before you go into the Task---it will give you the main thread ID
Then inside the Task call it again...this will give you the secondary thread ID.
Then Right before the Dispatcher call:
Console.WriteLine($"Do I have access to the label on this thread? {Label.Dispatcher.CheckAccess()}";
If you have access it will display True, if not it will display False...In your case it will display false because its owned by the other thread, but you can use the Dispatcher to be able to do work on that thread while in another thread...
Also, I recommend you not use Background Worker and use Tasks instead...this explains why in depth...basically Tasks do everything Background workers do and more, have less issues and are easier to work with...
http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html

As already commented by Ivan, remove the while loop while (worker.IsBusy) as it's blocking the UI thread to process further. As well, you should enable the WorkerReportsProgress to true
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
while (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
Per your comment, move those later processing to BackgroundWorker.RunWorkerCompleted Event

Related

BackgroundWorker not exiting loop

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.

C# Asynchronous Task for a Stock Ticker

I've been trying to learn more about asynchronous tasks and threading but not making a ton of headway.
I'm trying to load an "Engine" type of thread that will run in the background upon launch and be able to access the UI Thread to update variables, without hanging the UI Thread.
In the below code, Engine is called, and a Ticker object is created which holds the current value of (Litecoin/USD) called Last, also holds several other values that would be useful. This code successfully assigns the current value to label1.text. I don't necessarily need code but what approach would I take to create a ticker object in the background every second and update the UI thread with each new Ticker objects values.
Is this a good case for a background worker?
private void Form1_Load(object sender, EventArgs e)
{
Engine();
}
private void Engine()
{
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD:" + ltcusd.Last;
}
EDIT:
If I do the following, label1 throws an InvalidOperationException due to a Cross-thread operation attempt (label1 in the UI thread).
private void Form1_Load(object sender, EventArgs e)
{
var t = Task.Factory.StartNew(() => Engine());
t.Start();
}
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
}
Using async/await, the simplest way of getting an "asynchronous" sort of API is to invoke a new task. It's not great, but it'll make things simpler. I would probably create a new class which basically wrapped all the BtceApi methods in tasks:
public class BtceApiAsync
{
public Task<Ticker> GetTickerAsync(BtcePair pair)
{
return Task.Run(() => BtceApi.GetTicker(pair));
}
// etc
}
Then you can use a timer which fires once per second, which will start off a new task and update the UI appropriately:
// Keep a field of type System.Windows.Forms.Timer
timer = new Timer();
timer.Interval = 1000;
timer.Tick += DisplayTicker;
timer.Start();
...
private async void DisplayTicker(object sender, EventArgs e)
{
Ticker ticker = await BtceApiAsync.GetTickerAsync(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
Note that this doesn't mean the screen will be updated once per second... there will be a new task started once per second, and as soon as each task completes, the UI will be updated.
The use of await here - from an async method started on the UI thread - means you don't need to worry about using the UI; the whole async method will execute on the UI thread, even though the fetch itself happens in a different thread.
You can try ContinueWith to update the Label at the end of the task. If you want to update it event before the task ends then raise an event which is registered by on the UI thread. The event can then update the label.
I suppose this is Windows Forms. You could do it "old school style" and set the label text on the UI thread, and you can do that by passing delegate to the BeginInvoke or Invoke method.
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
UpdateText("LTC/USD: " + ltcusd.Last);
}
}
private void UpdateText(string text)
{
//Inspect if the method is executing on background thread
if (InvokeRequired)
{
//we are on background thread, use BeginInvoke to pass delegate to the UI thread
BeginInvoke(new Action(()=>UpdateText(text)));
}
else
{
//we are on UI thread, it's ok to change UI
label1.Text = text;
}
}

Cancel backgroundworker

I have UI which displaying status of long-running operations (downloading some text files from ftp) . For my purposes I use backgroundworker and I can't cancel operation.
void worker_DoWork( object sender, DoWorkEventArgs e )
{
try
{
int rowIndex = (int)e.Argument;
//begin UI update
StartWaitingBar(rowIndex);
//get provider id cell
GridViewDataRowInfo row = _proivderGridView.Rows[rowIndex];
GridViewCellInfo provIdCell = row.Cells[ "ProviderId" ];
var providerData = GetProviderData(Convert.ToInt32( provIdCell.Value));
var provider = ProviderFactory.CreateProvider(providerData);
provider.Synchronize();
e.Result = rowIndex;
}
catch (Exception exception)
{
return;
}
}
And code for worker creation:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync(args.RowIndex);
_syncWorkers.Add(providerId,worker);
...
var worker = _syncWorkers[providerId];
if(worker.IsBusy)
{
worker.CancelAsync();
}
else
{
worker.RunWorkerAsync(args.RowIndex);
}
Solution provided here seems not working for me beacuse it works for recurring operations (for which background worker is created, I suppose). Do I have to use threads(abort and join) for my purposes because I should provide possibilities for user to cancel long-running operation?
Need your advice.
Thanks in advance.
You cannot use Backgroundworker.CancelAsync() to cancel a long running I/O action. Like rifnl answered, the DoWork has to check worker.CancellationPending and set e.Cancel.
But you shouldn't use Thread.Abort() either. It could destabilize your process.
The solution you need has to come from provider.Synchronize(); somehow.
PS: and catch { return; } is horrible. Remove the entire try/catch and let the Bgw handle exceptions.
You've got to check e.Cancel within your DoWork method, which is missing from your code-snippet, but you've got to change your download method to an async call too, you're calling the method and wait for the answer within the dowork. Which is possible, but it won't check for the cancel flag in mean time.
Check the solution you posted (line 3):
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.Cancel)
{
// do something
}
_resetEvent.Set(); // signal that worker is done
}

Restart background worker

Is there a way to directly "restart" a background worker?
Calling CancelAsync() followed by RunWorkerAsync() clearly won't do it as their names imply.
Background info:
I have a background worker which calculates a total in my .net 2.0 Windows Forms app.
Whenever the user modifies any value which is part of this total I'd like to restart the background worker in case it would be running so that directly the latest values are considered.
The backgriound work itself does not do any cancleing.
When you call bgw.CancelAsync it sets a flag on the background worker that you need to check yourself in the DoWork handler.
something like:
bool _restart = false;
private void button1_Click(object sender, EventArgs e)
{
bgw.CancelAsync();
_restart = true;
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 300; i++)
{
if (bgw.CancellationPending)
{
break;
}
//time consuming calculation
}
}
private void bgw_WorkComplete(object sender, eventargs e) //no ide to hand not sure on name/args
{
if (_restart)
{
bgw.RunWorkerAsync();
_restart = false;
}
}
There are a couple of options, it all depends on how you want to skin this cat:
If you want to continue to use BackgroundWorker, then you need to respect the model that has been established, that is, one of "progress sensitivity". The stuff inside DoWork is clearly required to always be aware of whether or not the a pending cancellation is due (i.e., there needs to be a certain amount of polling taking place in your DoWork loop).
If your calculation code is monolithic and you don't want to mess with it, then don't use BackgroundWorker, but rather fire up your own thread--this way you can forcefully kill it if needs be.
You can hook the change event handler for the controls in which the values are changed and do the following in the handler:
if(!bgWrkr.IsBusy)
//start worker
else if(!bgWrkr.CancellationPending)
bgWrkr.CancelAsync();
Hope it helps you!
I want to leave my requests running, but no longer care about the results. I override the value of the background worker (my busy spinner is using the isBusy flag).
private void SearchWorkerCreate() {
this.searchWorker = new BackgroundWorker();
this.searchWorker.DoWork += this.SearchWorkerWork;
this.searchWorker.RunWorkerCompleted += this.SearchWorkerFinish;
}
private void SearchWorkerStart(string criteria){
if(this.searchWorker.IsBusy){
this.SearchWorkerCreate();
}
this.searchWorker.RunWorkerAsync(criteria);
this.OnPropertyChanged(() => this.IsBusy);
this.OnPropertyChanged(() => this.IsIdle);
}
May this method help someone... I've created a function to reset the backgroundworker in one method. I use it for task to do periodically.
By creating a Task, the backgroundworker is can be stopped with the CancelAsync and restarted inside the Task. Not making a Task wil start the backgroundworker again before it is cancelled, as the OP describes.
The only requirement is that your code runs through some loop, which checks the CancellationPending every period of time (CheckPerMilliseconds).
private void ResetBackgroundWorker()
{
backgroundWorker.CancelAsync();
Task taskStart = Task.Run(() =>
{
Thread.Sleep(CheckPerMilliseconds);
backgroundWorker.RunWorkerAsync();
});
}
Inside the backgroundworker I use a for-loop that checks the CancellationPending.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while(true)
{
if (backgroundWorker.CancellationPending)
{
return;
}
//Do something you want to do periodically.
for (int i = 0; i < minutesToDoTask * 60; i++)
{
if (backgroundWorker.CancellationPending)
{
return;
}
Thread.Sleep(CheckPerMilliseconds);
}
}
}

Winforms Progress bar Does Not Update (C#)

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.

Categories

Resources