ProgressBar update from another thread - c#

I know that this question had been asked 100 times before, but all the answers I read didn't worked for me. So, I'll try my luck and ask again.
I have a SliderBar that calls a method on the ValueChanged event.
In this method I do some stuff that takes time, and I want that in this time the user will see an "working" ProgressBar (IsIndeterminate=true).
Unfortunately, I don't succeed to make the ProgressBar start working (in the UI) until all the method loops finished.
I tried threads, BackgroundWorker and async Tasks but with no success..
What am I doing wrong?
This is the method:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e){
WorkingBar.IsIndeterminate = true; //change the progressBar
SqlAThread = new Thread(SqlAStart);
SqlAThread.Start();
}
The thread:
private void SqlAStart()
{
... //do some stuff
}

In Slider_ValueChanged you start a new tread to run the time-consuming SqlAStart() method, but SqlAStart immediately pushes the work back to the UI thread (via Dispatcher.Invoke()). Therefore, your UI hangs and you don't see any ProgressBar progress until the work is done.
In SqlAStart, only do a Dispatcher.Invoke() where you need to update the progress bar:
private void SqlAStart()
{
ServerThread = new Thread(HttpSrv.listen);
ServerThread.Start();
...
this.Dispatcher.Invoke((Action)(() => {
WorkingBar.Value = ...
}));
....
}

This is quite easy using IProgress<T>:
private async void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (State.Value > 0)
{
var progress = new Progress<int?>(value =>
{
WorkingBar.IsIndeterminate = (value == null);
if (value != null)
WorkingBar.Value = value.Value;
});
await Task.Run(() => doWork(progress));
Task.Run(SqlAStart);
}
...
}
private void SqlAStart(IProgress<int?> progress)
{
ServerTask = Task.Run(HttpSrv.listen);
...
}
private void doWork(IProgress<int?> progress)
{
if (progress != null)
progress.Report(null);
...
}
Note that you should (almost) never use Dispatcher, Thread, or BackgroundWorker in modern applications.

Rather than type out the whole lengthy process, I'd invite you to take a look at my answer to the Progress Bar update from Background worker stalling question here on StackOverflow. It demonstrates how to correctly use a BackgroundWorker object to update the progress value of a ProgressBar.
You may also like to view the BackgroundWorker Class page on MSDN for more detailed information.

Related

winforms: updating progress with a parallel.foreach

I haven't seen any posts pertaining to my issue, so I apologize if I post a question already asked.
I have a windows form program, c#, that checks stocks and does analysis. The main form launches another form, via a new thread and ShowDialog. While it's loading, it's running a parallel.foreach. In that parallel.foreach, I'd like to show progress on the main form.
I've run into cross-threading issues, and added invoke, although it doesn't appear to be thread-safe as it seems to be deadlocking toward the end of the parallel.foreach. I've tried delegates, events, no luck. Help me Obi-Wans, you're my only hope!
Stripped down version:
Main form
private void button1_Click(object sender, EventArgs e)
{
YearLows yearLows = new YearLows();
Thread yearLowsThread = new Thread(() => StartYearLows(yearLows));
yearLowsThread.Start();
btnGetYearLows.Enabled = false;
}
private void StartYearLows(YearLows yearLows)
{
yearLows.ShowDialog();
}
public void UpdateProgress(string text)
{
lblProgress.Text = text;
}
2nd form dialog
public partial class YearLows : Form
{
private void YearLows_Load(object sender, EventArgs e)
{
// work
Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
});
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => frmMain.UpdateProgress(text)));
}
}
Update 1:
If I move the progress update label to the child form, it appears I am getting all the progress updates. I had to move from the Load event to the Shown event so that the form renders, so users can see the progress updates. I had to follow SLaks advice though and run Task.Run(() => Parallel.ForEach. This will work for me. Would still like to figure out why it still locks up toward the end if I wanted the progress updates on the main form. (I've always read async void was bad, but I guess no way around this in these defined method signatures in winforms)
public partial class YearLows : Form
{
private async void YearLows_Shown(object sender, EventArgs e)
{
await AnalyzeStocks();
}
private async Task AnalyzeStocks(object sender, EventArgs e)
{
// work
await Task.Run(() => Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
}));
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => lblProgress.UpdateProgress(text)));
}
}
Parallel.ForEach is a blocking call; it runs delegates on the calling thread too. Therefore, the UI cannot update until it finishes.
Instead, you should use await with Task.WhenAll (if you're doing async work) or Task.Run(() => Parallel.ForEach(...)) (if it's CPU-bound) so that you leave the UI thread idle and able to update.
you can use Async Await function for this puprose... this link can be more useful to you...
PictureBox animation freezing while Task running
As per SLaks answer, an example of using Task.Run, with UI update
var tasks = new List<Task>();
foreach (var result in results)
{
tasks.Add(Task.Run(async () => {
// DO WORK
Dispatcher.Invoke(() =>
{
// UPDATE THE UI, I.E. ProgressBar.Value++;
});
}));
}
await Task.WhenAll(tasks);

C# WPF Indeterminate progress bar

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

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;
}
}

WPF/C# Don't block the UI

I've an existing WPF application, which has several sections. Every section is a UserControl, that implements an interface.
The interface specify two methods: void LoadData([...]) and bool UnloadData().
Those method are called by the UI thread, so we need to do our work in backgroundworker if it's time consuming.
No problems with LoadData since we can update the UI asynchronously. The problem is with UnloadData().
This should return if we can really leave the current view.
This is computed with the current status of data(Saved/modified/Invalid):
Saved return true,
Invalid asks if you want to stay to save some
correct data or leave without saving
Modified tell you that you can
either cancel your change(return true), either continue to
edit(return false), either save you current data(return true)
The problem is with the "Modified -> Save". This is a time consuming method, so to respect the philosophy of the application, we should run this in a background thread(with a busy indicator).
But if we just launch the thread and go to the next section, it will return "true" to the method call, and we will directly launch the next view.
In my case, loading the next view before our local data is saved can be a problem.
So:
Is there a way to wait on the background thread to finish before returning "true", WITHOUT blocking the UI?
public bool UnloadData(){
if(...){
LaunchMyTimeConsumingMethodWithBackgroundWorker();
return true;//Only when my time consuming method ends
}
//[...]
}
Important EDIT
Maybe I wasn't clear enought: I know how to use a BackgroundWorker, or TPL. My problem is that the parent class(the one which call the UnloadData()" is a class that I cannot edit(for multiple reasons: It's in another DLL that will not be reloaded, it already works with 70+ userControls, all in separate projects(dll), loaded by reflection.
This wasn't my choice, I don't find it good, but I've to deal with it now. I'm mostly looking for way to make my method wait on the return of my method. I'm not sure if it is possible. But I'm looking for a workaround, it will spare me weeks of works.
Ok now I'm excited, because I think I may have discovered something on my own...
So, what you do is this: You create a DispatcherFrame, push that frame onto the Dispatcher, and in the RunWorkerCompleted you set the Continue of the Frame to false.
This is the code so far:
public void Function()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += TimeConsumingFunction;
var frame = new DispatcherFrame();
worker.RunWorkerCompleted += (sender, args) =>
{
frame.Continue = false;
};
worker.RunWorkerAsync();
Dispatcher.PushFrame(frame);
}
private void TimeConsumingFunction(object sender, DoWorkEventArgs doWorkEventArgs)
{
Console.WriteLine("Entering");
for (int i = 0; i < 3; i++)
{
Thread.Sleep(1000);
}
Console.WriteLine("Exiting");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Function();
Console.WriteLine("Returns");
}
You should implement a dependency property "IsBusy" of type bool, that you set to TRUE before starting the BackgoundWorker, and then to FALSE when the work is complete.
On the UI, you bind to that property whatever functionality you want disabled during the processing(like the button for loading the next view, etc.); or maybe showing a "Cancel" button.
You should not "wait" for the operation to complete, you can retrieve the result in an additional variable, that the BackgroundWorker will set:
BackgroundWorker _bw;
bool _returnValue = false;
private void button_Click(object sender, RoutedEventArgs e)
{ // if starting the processing by clicking a button
_bw = new BackgroundWorker();
IsBusy = true;
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
_bw.RunWorkerAsync();
}
void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IsBusy = false;
// retrieve the result of the operation in the _returnValue variable
}
void _bw_DoWork(object sender, DoWorkEventArgs e)
{
_returnValue = UnloadData();
}
private bool UnloadData()
{
if (...)
{
LaunchTimeConsumingMethod();
return true;
}
else
return false;
//etc ...
}
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
// Using a DependencyProperty as the backing store for IsBusy. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register( ... )
You may be able to try using the new "await" features of .NET 4.5.
The await keyword allows you to await the completion of a Task object, without blocking the UI.
Try this modification:
public async bool UnloadData()
{
if(...)
{
await Task.Factory.StartNew(() =>
{
LaunchMyTimeConsumingMethod();
});
return true;//Only when my time consuming method ends
}
//[...]
}
Treat UnloadData as a async operation and let the async/await features handle both the case when it completes synchronously and when it needs to complete asynchronously:
public async Task<bool> UnloadData(){
if(...){
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool
await Task.Factory.StartNew(()=>LaunchMyTimeConsumingMethodWithBackgroundWorker());
// The return statement is the continuation and will run in the UI thread after the consuming method is executed
return true;
}
// If it came down this path, the execution is synchronous and is completely run in the UI thread
return false;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
bool result = await this.UnloadData();
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
EDIT
If you can't modify the UnloadData, then just execute it on the ThreadPool, as #BTownTKD noted:
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool, whether it takes the long or the short path
bool result = await The Task.Factory.StartNew(()=>this.UnloadData());
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
You probably should use TPL if your framework version is 4.0:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // this will work only if you're running this code from UI thread, for example, by clicking a button
Task.Factory.StartNew(() => UnloadData()).ContinueWith(t => /*update ui using t.Result here*/, uiScheduler);
Hope this helps.
You have to implement a callback function (RunWorkerCompleted), this is called when the background worker finishes.
Check out an example here:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

progressBar separate thread

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)

Categories

Resources