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 ;)
Related
I am trying to understand better how can I update a windows forms progress bar from an async operation but I am getting some unexpected behavior from that.
Basically I am having a button which should after is being clicked to update a progress bar and then set it back to 0 once the progress bar gets 100% updated.
This is my code:
private async void button1_Click(object sender, EventArgs e)
{
await CallMethodAsync().ContinueWith((prevTask) =>
{
prevTask.Wait();
progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; }));
});
}
private static async Task ExecuteMethodAsync(IProgress<double> progress = null)
{
double percentComplete = 0;
bool done = false;
while (!done)
{
if (progress != null)
{
progress.Report(percentComplete);
}
percentComplete += 10;
if(percentComplete == 100)
{
done = true;
}
}
}
private async Task CallMethodAsync()
{
var progress = new Progress<double>();
progress.ProgressChanged += (sender, args) => { progressBar1.Increment(10); };
await ExecuteMethodAsync(progress);
}
Having this implementation the progress bar is not being updated at all even if I call "Wait()" on the operation that should update the value of the progress bar.
If i remove this part of code:
progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; }));
the progress bar gets updated but it remains all the time like that, and I want to set it back to 0 once it was entirely filled so that I can update it again when I click again the button.
Could someone please explain me what am I doing wrong ?
One of the reasons async-await syntax was invented because it was difficult to follow the sequence of instructions when tasks were concatenated using functions like ContinueWith.
If you use async-await it is seldom necessary to use statements like ContinueWith. After an await, the thread already continues with the statements after the await.
If the button is clicked, you want to call ExcecuteMethodAsync. This function takes an IProgress, because it wants to report progress regularly. You want to call this function asynchronously, so whenever the function has to wait for something, it doesn't really wait, but returns control to you so you could do other things instead of really waiting, until you encounter an await, in which case your caller continues processing until he encounters an await, etc.
The nice thing with async-await is that the thread that continues after your call to an async function has the same context as the calling thread. This means that you can regard it as your original thread. No InvokeRequired, no need to protect data with mutexes etc.
Your function could be simplified as follows:
async Task CallMethodAsync()
{
var progress = new Progress<double>();
progress.ProgressChanged += OnProgressReported;
await ExecuteMethodAsync(progress);
}
private void OnProgressReported(object sender, ...)
{
// because this thread has the context of the main thread no InvokeRequired!
this.progressBar1.Increment(...);
}
private async void button1_Click(object sender, EventArgs e)
{
await CallMethodAsync();
}
So when the button is clicked, CallMethodAsync is called. This function will create A Progress object and subscribes on its Report event. Note that this is still your UI-thread. Then it calls ExecuteMethodAsync, which will regularly raise event Report, which is handled by OnProgressReported.
Because ExecuteMethodAsync is async, you can be sure there is somewhere an await in it. This means that whenever it has to await, control returns to the caller, which is CallMethodAsync, until is encounters an await, which in this case is immediately.
Control goes up the call stack to the caller, which is button1_click, where it immediately encounters an await, so control goes up the call stack, etc.
All these controls have the same context: it is as if they are the same thread.
An article that helped me a lot to understand async-await is this interview with Eric Lippert. Search somewhere in the middle for async await
Another articel that helped me a lot to learn good practices were this article by the ever so helpful Stephen Cleary and Async/Await - Best Practices in Asynchronous Programming also by Stephen Cleary
Your issue is happening because ExecuteMethodAsync(...) is not actually asynchronous.
Add the following before the while loop to make it asynchronous
await Task.Delay(1);
or enclose some synchronous portion of code (e.g. the while loop) into a:
await Task.Run(() => { ... });
or (the best one), add the following at the beginning of the function:
await Task.Yield(); // Make us async right away
Throughout this question, I've included some links which show that I've done some work searching for a solution.
I'm developing a UWP app with touchscreen and GPIO.
UI has a stop button, a reset button, and a textblock. GPIO is used for a physical start button, a motor, and 2 limit switches. Motor can rotate until it runs into a limit switch.
Code to control the hardware (e.g., Motor.Forward()) has been written and tested, but is excluded from this question for brevity. Code for the stop button is excluded for the same reason.
If the steps in these methods would perform synchronously... desired behavior might be described by the following code:
//Event handler for when physical start button is pushed
private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
Start();
}
private void Start()
{
//Update UI
stopButton.IsEnabled = true;
resetButton.IsEnabled = false;
textBlock.Text = "Motor turning";
Motor.Forward();
while(Motor.HasNotHitEndLimit())
Motor.Off();
//Update UI
stopButton.IsEnabled = false;
resetButton.IsEnabled = true;
textBlock.Text = "Task complete";
}
//Event handler for reset button
private void btnReset_Click()
{
//Update UI
stopButton.IsEnabled = true;
resetButton.IsEnabled = false;
textBlock.Text = "Motor turning";
Motor.Back();
while(Motor.HasNotHitStartLimit())
Motor.Off();
//Update UI
stopButton.IsEnabled = false;
resetButton.IsEnabled = true;
textBlock.Text = "Reset complete";
}
If I recall correctly, UI updates within "private void btnReset_Click()" work, but they are not synchronous... I.e., all of the UI updates were completing right after "btnReset_Click()" finished.
From reading answers to similar questions... it seems that UI updates within "Start()" fail because I'm not on the UI thread ("The application called an interface that was marshalled for a different thread.").
It seems that Task Asynchronous Pattern is a common answer to these types of questions. However, my attempts to do this have yielded strange results...
The code below is the closest I've come to the desired result. I added async tasks that use CoreDispatcher to handle UI updates.
//Task for updating the textblock in the UI
private async Task UpdateText(string updateText)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { textBlock.Text = updateText; }));
}
//Task for enable/disable a given button
private async Task UpdateButton(Button btn, bool shouldThisBeEnabled)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { btn.IsEnabled = shouldThisBeEnabled; }));
}
//Event handler for when physical start button is pushed
private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
Start();
}
private void Start()
{
//Update UI
UpdateButton(stopButton,true).Wait();
UpdateButton(resetButton,false).Wait();
UpdateText("Motor turning").Wait();
Motor.Forward();
while(Motor.HasNotHitEndLimit())
Task.Delay(1).Wait();
Motor.Off();
//Update UI
UpdateButton(stopButton,false).Wait();
UpdateButton(resetButton,true).Wait();
UpdateText("Task complete").Wait();
}
//Event handler for reset button
private async void btnReset_Click()
{
//Update UI
await UpdateButton(stopButton,true);
await UpdateButton(resetButton,false);
await UpdateText("Motor turning");
await Task.Delay(1);
Motor.Back();
while(Motor.HasNotHitStartLimit())
await Task.Delay(1);
Motor.Off();
//Update UI
await UpdateButton(stopButton,false);
await UpdateButton(resetButton,true);
await UpdateText("Reset complete");
}
Problems/idiosyncrasies with the code above (besides any beginner mistakes I might be making due to just starting out with C#... and the fact that it seems overly complicated and confusing):
-In "Start()" I use .Wait() on the tasks (because it seems to work, I don't really understand why...), and in btnReset_Click() it worked best to await them...
-btnReset_Click() is not synchronous. UI updates appear to be "one step behind"... I.e., in debug mode, the stop button enables when I step over "await UpdateButton(resetButton,false)", reset button disables when I step over "await UpdateText("Motor turning")", and so on.
-Regarding btnReset_Click()... The while loop lasts MUCH longer than 1 millisecond in real time, yet if I remove all "await Task.Delay(1)" then the UI updates are "one step behind". With "await Task.Delay(1)" included, the UI updates get "caught up" to where they should be. Why does "await Task.Delay(1)" affect UI updates this way?
If any knowledgeable folks are willing to address some/all of this question and maybe let me prod them for details about their answer(s), I'd be very grateful!
Bonus question.... I also have a "Toggle Test Mode" button on the touchscreen which enables one list of UI buttons and disables another (based on a static bool "testmode"). I don't need to use TAP to update the UI here, but recall that I want to do this synchronously (even though it seems pointless in this example).
private async void btnTestMode_Click(object sender, RoutedEventArgs e)
{
testMode = !testMode;
if (testMode == true)
{
await UpdateButtons(TestModeButtons,true);
await UpdateButtons(NormalModeButtons,false);
return;
}
await UpdateButtons(TestModeButtons,true);
await UpdateButtons(NormalModeButtons,false);
}
private async Task UpdateButtons(List<Button> btns, enable)
{
foreach (var btn in btns)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { btn.IsEnabled = enable; }));
}
}
As it's written above, this behaves like btnReset_Click()... where the UI updates are "one step behind". However, if I add ".ConfigureAwait(false)" to each await in the event handler, then it becomes synchronous. I've done some reading on this topic, but don't fully understand it yet, and I would love for someone with a better understanding to help me understand it as it relates to my project.
You should not be doing any of that Dispatcher.Run and similar...
First stop and think and understand what your problem is and why the UI does not update.
Create a new thread where you control your motors (separate from the UI thread).
On the button clicks, call method on your motors thread.
When there are events on the motors thread where you need to update the UI, call (synchronously?) methods on the UI thread.
In a nutshell, consider these tips as you build your app:
Never call .Wait() or Result or otherwise try to block on an asynchronous operation on a UI dispatcher thread
If you do want to create worker threads to do blocking operations, try await Task.Run(...) for its simplicity (you can create raw threads but it's more work). Same for busy-waits like while(notDone) ; // empty
From a background thread (such as one created by Task.Run), if you want to update the UI then you would use Dispatcher.RunAsync(...) but only to set the properties of your UI
To disable your UI while a background thread does work, set IsEnabled=false or add a top-most emi-transparent interaction shield etc.
Also, try starting with something simple (eg, no hardware access; just use Task.Delay(...).Wait() to simulate blocking on the hardware). Once you have the UI basically working you can plug in the hardware calls.
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 am trying to implement an indeterminate progress bar into my program. I'm new to threading, but as far as I know one of the best options here is to add an async method, and await the "heavy" function to perform its results. So I wrote this:
public void Window_Loaded(object sender, RoutedEventArgs e)
{
firstLoad();
}
private async void firstLoad()
{
LW.Title = "Loading...";
LW.Show();
filterTextBox.Text = defaultSearch;
await Task.Run(() => InitializeFilter());
}
private void InitializeFilter()
{
//Asynchronous???
Dispatcher.BeginInvoke(new Action(() => {
//... some lines of code that takes some time to run.
dataGrid.ItemContainerGenerator.StatusChanged += new EventHandler(closeLoadingWindow);
}));
private void closeLoadingWindow(object sender, EventArgs e)
{
if (LW != null)
{
LW.closable = true;
LW.Close();
}
}
firstLoad runs when the window is loaded, showing an indeterminate LW loadingWindow, and running the InitializeFilter() method (the heavy one). Finally, when the grid is populated and loaded, an event fires, allowing the LW window to be closed and closing it (if I didn't make it unclosable, a funny user could just close it clicking or using F4, which is not nice).
The system is working properly and everything works as expected regarding time frames, but the loading bar is frozen, not showing progress. The same LW bar works in the MainWindow with a similar set up What am I missing? Thanks in advance!
as far as I know one of the best options here is to add an async method, and await the "heavy" function to perform its results
The best option is to use Task.Run to move the heavy processing to the thread pool, and use await to retrieve its results.
The code as it currently stands uses Task.Run to move to the thread pool and then immediately turns around and uses Dispatcher to move back to the UI thread before doing the heavy processing. Thus, it's blocking the UI thread.
what this particular DataGrid displays is a CollectionView, which is not thread-safe.
Right, you can't update data-bound objects from a thread pool thread.
The best solution is to separate the heavy processing from the UI updates, something like this:
public async void Window_Loaded(object sender, RoutedEventArgs e)
{
await firstLoadAsync();
}
private List<FilterType> InitializeFilter()
{
//... some lines of code that takes some time to run.
}
private async Task firstLoadAsync()
{
LW.Title = "Loading...";
LW.Show();
filterTextBox.Text = defaultSearch;
var filterData = await Task.Run(() => InitializeFilter()); // Get the plain data on a background thread
myCollectionView = new CollectionView(filterData); // Update the UI
if (LW != null)
{
LW.closable = true;
LW.Close();
}
}
do not use your dispatcher. Microsoft had the foresight to use it's magic (SynchronizationContext) to be able to update the UI thread in a method that is being executed in an async context. This is demonstrated in their async/await example found here
while under previous/other circumstances, you would have to either marshal back to the main (UI) thread to update the UI thread, or wait until completed and retrieve the results from objects who share state. Since you are using async/await then you should be fine to not use the dispatcher, and update the UI directly.
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.