I have a method tied to a button that does time consuming thing. So I have created a label and binded it to property to inform user that application is actually doing something.
private void Button_OnClick(object sender, RoutedEventArgs e)
{
_viewModel.BottomBarMessage = "Loading..."; //part 1
//do very long thing, for example
System.Threading.Thread.Sleep(3000); //wait for 3 seconds to simulate long operation
_viewModel.BottomBarMessage = "Done."; //part 2
}
Issue is part 1 does not seem to be ever executed. Label switches to Done but never to Loading.
I have suspected its because UI gets blocked, but changing Sleep to be executed as Task changes nothing.
use await Task.Delay instead of Thread.Sleep - you are blocking UI thread.
Something like:
private async void Button_OnClick(object sender, RoutedEventArgs e)
{
_viewModel.BottomBarMessage = "Loading..."; //part 1
//do very long thing, for example
await Task.Delay(3000);
_viewModel.BottomBarMessage = "Done."; //part 2
}
Related
I have been programming a button_Click event, where I need the following:
Set visibility of a gif to true (in picture box, was false at first)
Wait one second with the picture shown (its a gif and I need it moving, which means System.Threading.Thread.Sleep(1000) will stop the animation)
Exit program
I have this:
private void button1_Click(object sender, EventArgs e)
{
loading.Visible = !loading.Visible;
// pause for a second, then exit
Environment.Exit(0);
}
Thanks!
You may use the following:
// ↓↓↓↓↓ << notice the `async` keyword.
private async void button1_Click(object sender, EventArgs e)
{
loading.Visible = !loading.Visible;
// Wait for one second.
await Task.Delay(TimeSpan.FromSeconds(1));
Application.Exit();
}
The Task.Delay() method does the following:
Creates a task that will complete after a time delay.
And using the await keyword, we asynchronously wait for the task to complete. "asynchronously" means that the current thread (the UI thread in this case) will not be blocked. Therefore, the animation will not be affected.
I have already tried several online examples (Thread, Dispatcher, await/async) but none is working for me in my C#/WPF project.
I have the following button click method:
private void BtnInstall_Click(object sender, RoutedEventArgs e)
{
this.lblResponse.Content = "";
executeInstall(); //do some work
this.lblResponse.Content = "DONE";
}
The label gets updated afterwards to DONE, but when I click again on the button the label isnt getting emptied before the processing of executeInstall.
As I mentioned I already tried several different examples from other questions (Dispatcher.BeginInvoke, Thread, Task, await/async) but none of them has worked - the label change before is never done before the processing of executeInstall.
I am working in .NET framework 4.7.2.
Is there maybe a setting that debug mode only executes the program with one thread and thats maybe why none of the solutions works for me?
Use async for that.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
this.lblResponse.Content = "";
await Task.Run(()=> executeInstall());
this.lblResponse.Content = "DONE";
}
UPDATE: If you need to access the UI inside your executeIntall method you will need to invoke the Dispatcher. In this case you would need to delay the Task to give the label time to update before the install starts. Note that this will cause the UI to freeze during the entire install.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
lblResponse.Content = "starting...";
await Task.Delay(100).ContinueWith(_=>
{
App.Current.Dispatcher.Invoke(() =>
{
executeInstall();
lblResponse.Content = "DONE";
});
});
}
A better approach would be to only call the dispatcher when it's actually needed. This would keep the UI responsive during the entire process.
private async void BtnInstall_Click(object sender, RoutedEventArgs e)
{
lblResponse.Content = "starting...";
await Task.Run(()=> executeInstall());
lblResponse.Content = "DONE";
}
private void executeInstall()
{
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Downloading Files...");
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Unzipping Files...");
Thread.Sleep(1000); //do time consuming operation
App.Current.Dispatcher.Invoke(() => lblResponse.Content = "Updating Files...");
Thread.Sleep(1000); //do time consuming operation
}
I know that for async operations it is possible to track its progress, but I will try that later. Now I have a simple window forms apply with a button (or a pair of buttons - the number does not matter). The buttons call an async operation
public async void Button1_Click(...)
{
await Button1_OperationAsync(...);
}
If I don't press the button nothing is going on but if I press it once the Button1_OperationAsync starts (and is awaited). (I am not really sure if to call it "a thread").
But what happens if I press the button twice? Well then before the first async operation finishes, the Button1_OperationAsync is called again. (Or if another similar button is pressed then a Button2_OperationAsync is called)
Maybe even the second async operation would finish before the first one.
What I want is a simple way of knowing if any operation is going on. So what I thought is to have a variable and increment it when an operation is called and decrement it when an operation is finished. Something like
int numberOfOps=0;
public async void Button1_Click(...)
{ numberOfOps++;
textBox1.Text="Started!";
await Button1_OpeationAsync(...);
numberOfOps--;
if(numberOfOps<=0)
{
textBox1.Text="Done!";
}
}
Be aware that this code would go in the other button (Button2) too. Or many other buttons.
I am aware that issues of synchronization might be involved. So I would appreciate advice on what I am trying to do in order to do correctly
When using async/await you're not using any threads for the UI code other than the UI-thread. It's possible that the code that gets called in the Button1_OpeationAsync method might use a separate thread, but the calling code will remain on the UI thread.
Try having a play with this code:
private int numberOfOps = 0;
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = $"Started! {++numberOfOps}";
await Task.Delay(TimeSpan.FromSeconds(5.0));
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
It works just fine. You can use the numberOfOps variable across multiple buttons.
If you'd like to make it easy to re-use the code, try it this way:
int numberOfOps = 0;
private async Task RunOp(Func<Task> op)
{
textBox1.Text = $"Started! {++numberOfOps}";
await op();
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
private async void button1_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button1_OpeationAsync(...));
}
private async void button2_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button2_OpeationAsync(...));
}
Have a task array, and a task object at class level:
private List<Task> tasks = new List<Task>();
private Task task = null;
In each of your click handlers do something like this:
var operationTask = SomeOperationAsync(...);
tasks.Add(operationTask);
task = Task.WhenAll(tasks);
if (task.IsCompleted)
{
// no operation is going on
tasks.Clear();
// do what ever you want to do further
}
else
{
//some operation is going on
}
Please could someone suggest why the following doesn't work? All I want to do is display an indeterminate progress bar which starts when the button is clicked, then after I've done some work I set the indeterminate progress bar to false to stop it.
However when I run the code below the indeterminate progress bar doesn't even start. I tried commenting out the line this.progressBar.IsIndeterminate = false and when I do this the progress bar does start but then doesn't terminate.
private void GenerateCSV_Click(object sender, RoutedEventArgs e)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate ()
{
this.progressBar.IsIndeterminate = true;
// do some work
Thread.Sleep(10 * 1000);
this.progressBar.IsIndeterminate = false;
}));
}
Your code can't work because the "do some work" is happening on the same Thread on which the UI works. So, if that Thread is busy with the "work", how can it handle the UI animation for the ProgressBar at the same time?
You have to put the "work" on another Thread, so the UI Thread is free and can do its job with the ProgressBar (or other UI controls).
1) create a method that does the work and returns a Task, so it can be "awaited" for completion:
private async Task DoWorkAsync()
{
await Task.Run(() =>
{
//do some work HERE
Thread.Sleep(2000);
});
}
2) Put the async modifier on the GenerateCSV_Click:
private async void GenerateCSV_Click(object sender, RoutedEventArgs e)
3) throw away all that "Dispatcher" / "Invoke" etc stuffs, and use the DoWorkAsync this way:
private async void Button_Click(object sender, RoutedEventArgs e)
{
PB.IsIndeterminate = true;
await DoWorkAsync();
PB.IsIndeterminate = false;
}
So, what's happening now? When GenerateCSV_Click encounters the first await... it begins to work automatically on another Thread, leaving the UI Thread free to operate and animate the ProgressBar.
When the work is finished, the control of the method returns to the UI Thread that sets the IsIndeterminate to false.
Here you can find the MSDN tutorial on async programming:
https://msdn.microsoft.com/en-us/library/mt674882.aspx
If you Google "C# async await", you'll find dozens of tutorials, examples and explanations ;)
I have a very simple form with a progress bar on it and a delete button. When the user clicks "Delete", a stored procedure runs for around five minutes.
I would like a simple marquee progress bar to run but it never appears. I read all the other questions about this but they all required a background worker.
Is it possible to make a marquee progress bar without a background worker?
public partial class ProgressBarSample
{
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
}
}
Your code
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
prevents windows messages from being processed by your application until the delay completes because you block the thread responsible for processing those messages. Windows relies on those messages being processed in a timely manner.
The result is that your user interface will appear unresponsive to the user.
There are a number of mechanisms that allow you to do your processing on a second thread. BackgroundWorker is one that was commonly used at the time WinForms first came out and still a solid choice. You can use any technique that does the long-running work on another thread, but you must do it on that other thread.
You can use the async pattern to simplify the coding for that other thread
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
await Task.Run(() =>
{
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
});
progressBar1.Visible = false;
}
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces your problem, it's impossible to say for sure what the issue is. However, the code you posted won't work. Your btnDelete_Click() method sets the Visible property to true, but then immediately sets it back to false, because the Task.Delay() method doesn't actually block.
Probably what you want is this:
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
await Task.Delay(10000);
progressBar1.Visible = false;
}
BackgroundWorker is the generally accepted method for doing such background work, hence the name; but for a primitive "show for X period of time", you could use a Timer that checks the amount of time passed since (in this case) delete was last clicked to see if it should hide the control (and disable itself, no use ticking when there is nothing to do.)
Something like:
public partial class ProgressBarSample
{
TimeSpan pbShowDuration = [blah blah];
DateTime pbShowFrom = DateTime.MinDate;
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
pbShowFrom = DateTime.Now;
timer1.Enabled = true;
// run stored procedure that takes around 5 minutes
}
private void timer1_Tick(object sender, EventArgs e)
{
if ((DateTime.Now - pbShowFrom) > pbShowDuration)
{
timer1.Enabled = false;
progressBar1.Visible = false;
}
}
}
But how are you planning to update the progress bar?
With the new task, sync, await features in C# you have a lot of options. If you don't need to do anything other than let the user know the operation is done you can start your progress bar, then start a task that runs your proc and when it's done stop the progress bar. I would personally put something other than a progress bar. To me a progress bar means you have a finite amount of time to wait. If your SP can vary in time I would go with some kind of busy display icon or something like that.
I personally would useh Task.ContinueWith in this case.
This MSDN article show a great way to handle it.
https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx
Background worker is an old method which is superseded by using Tasks. Tasks have more functionality, can do things backgroundworkers cannot, and are much simpler to use.