The desired scenario:
When I click on the button, I want it to be hidden until async call is completed.
I have a button in xaml like this
<Button Name="btnLoadNextTransactions" Content="Button" Click="btnLoadNextTransactions_Click" Visibility="{Binding LoadMore, Converter={StaticResource converter}}" />
and a click event to
private void btnLoadNextTransactions_Click(object sender, RoutedEventArgs e)
{
App.ViewModel.LoadMore = false;
ApplicationBl<Transaction>.GetDataLoadingCompleted += GetDataLoadingCompleted;
ApplicationBl<Transaction>.GetData(++offset*10, 10);//works only if I comment out this line
App.ViewModel.LoadMore = true;
}
This only works if I comment out async call
//ApplicationBl<Transaction>.GetData(++offset*10, 10);
But that's not a feature I want to comment out :)
I know I'm missing some delagete or dispatcher. I just started coding with SL.
You need to put LoadMore = true in the GetDataLoadingCompleted method.
What's happening is that the line
ApplicationBl<Transaction>.GetData(++offset*10, 10);
Isn't blocking the dispatch thread, so the LoadMore=true get's called right away. The easiest way to do it would probably be with a delegate that you call after getting the data.
So you would change your GetData method to look like this:
public void GetData( int offset, int pageSize, Action callback)
{
//Existing code.
//Notify the callback that we are done.
callback();
}
Once that done just call the method like so:
ApplicationBl<Transaction>.GetData(++offset*10, 10, () =>
{
Deployment.Current.Dispatcher.BeginInvoke(() => App.ViewModel.LoadMore = true;);
});
The reason why you'll need to use the Dispatcher is that the callback is being executed in a background thread, and since the LoadMore property is effecting Gui Elements it needs to be done on the UI thread.
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
When user clicks on Execute button, I want to do some stuff and output log messages to TextBlock progressively - so user can see what is currently happening.
The problem is that my TextBlock changes it content after all work is finished (too late). How can I force WPF to repaint itself during process ?
Code looks like this:
private void btn_execute_Click(object sender, RoutedEventArgs e)
{
.... stuff ....
}
I have tried adding output_log.InvalidateVisual(); after any change to TextBlock, didn't work as expected.
If you run synchronous code in a Click handler of a Button, this code is being executed in the Dispatcher thread and thus prevents the Dispatcher from running any other code like displaying the changes of your messages in a TextBlock.
There are (at least) three possible ways to solve this issue.
First, you can run your Execute code in another Thread, Task or async event handler and set the Text using the Dispatcher:
private async void btn_execute_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 100; i++)
{
// Simulate doing some stuff...
await Task.Delay(100);
// Thanks to async/await the current context is captured and
// switches automatically back to the Dispatcher thread after the await.
output_log.Text += i + ", ";
// If you were using Task.Run() instead then you would have to invoke it manually.
// Dispatcher.Invoke(() => output_log.Text += i + ", ");
}
}
The main advantage is that you are not blocking the Dispatcher - which is highly recommended for everything you do.
Second, you can keep doing your Execute code in the Dispatcher, but then you have to "flush" the Dispatcher every time when you want to refresh your text, so that it can handle all waiting UI actions:
private void btn_execute_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 100; i++)
{
// Simulate doing some stuff...
Thread.Sleep(100);
output_log.Text += i + ", ";
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
}
}
This is certainly possible but I really wouldn't recommend it.
Or third,
you can use MVVM for your architecture,
run your Execute code in an async event handler (or Command),
change only the LogText property of your ViewModel and
use data binding to bind the TextBlock.Text to this MyLogViewModel.LogText property.
Unfortunately I can't give you a quick sample code for this scenario, but it's surely worth thinking about it because MVVM is just a really natural architecture for any kind of WPF application.
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 have seen a lot of questions about how to edit controls on c# form from a different thread but none make much sense to me. I understand that you can not change any UI from another thread than it's main. To make this work you have to use invoke and from there safely edit the control?
I have a button that starts writing in a file and the moment you press the button the button itself gets disabled so you can not start multiple threads that do exactly the same. When the writing is done I want the button to be available again but I can not get it working on this other thread.
I have this as the Generate_Click event from the form.
private void Generate_Click(object sender, EventArgs e)
{
Generate.Enabled = false;
int x = 512;
int y = 512;
MBrot mbrot = new MBrot(x, y);
PB_Update lb = new PB_Update(0, y, Generator_PB, Generate, mbrot, this);
lb.Start();
}
And this is in PB_Update.cs the ThreadWork() function, when the while loop is done the writing to the file is done and so is the thread so its ended and given a messagebox with "finished" now as last the button needs to be enabled again.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.Enabled = true;
}
For WinForms you can execute directly on the thread which the control was created on through the Control.BeginInvoke method, you can use Control.Invoke as well but, Control.BeginInvoke is preferred for UI operations.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.BeginInvoke((Action)delegate()
{
Generate_.Enabled = true;
});
}
Somehow, get a reference to the form that hosts the generate_ button (let's call it myform). Then, at the bottom of your ThreadWork:
myform.Invoke(new Action(() => {
myform.SetGenerateEnabled();
}));
And then inside your form create that method that enables the button appropriately. (I used a method rather than just updating the button directly so that you don't publicly expose the button.)
This executes the commands inside the { ... } on myform's thread, which is a UI thread, because it is UI. At least, that's what I understand. This is how I do all of my UI updating from other threads.
Here's a simple example of a way to kick off an async task that disables a button for 5 seconds and then enables it again. Meanwhile, the rest of the UI is functional.
Note that this async method exists in the same class as your Generate_Click event, and runs on the UI thread. This means that it can enable and disable the button. But the long running task executes on a separate thread, so it doesn't lock the UI.
Hopefully this sample provides you a base to modify for your own code:
private void Generate_Click(object sender, EventArgs e)
{
DisableButton(sender as Button, 5);
}
private async void DisableButton(Button sender, int secondsToDisable)
{
sender.Enabled = false;
// In your code, you would kick off your long-running process here as a task
await Task.Run(()=>Thread.Sleep(TimeSpan.FromSeconds(secondsToDisable)));
sender.Enabled = true;
}
I'm playing around with WPF. I am limited to the .Net framework 3.5. I want to update a text box with simple status text while I run some long method. No matter what I try, I cannot seem to get the text box to update until the long method has completed. I have tried threading / using the controls dispatcher etc. In the example below, I have reverted back to simply hiving off the long method to a thread but it still wont work. The TextStatus textbox never gets updated until after the long method (LoadDevices) has completed. Can someone tell me how to do this? Any help much appreciated.
private void UpdateButton_Click(object sender, RoutedEventArgs e)
{
UpdateStatus("Searching for devices, please wait . . .");
var t = new Thread(LoadDevices);
t.Start();
}
private void UpdateStatus(string status)
{
TextStatus.AppendText(status);
TextStatus.InvalidateVisual();
}
I think you are not providing enough code to figure out the problem. Still, fact is that your UI is blocked.
Try the following, maybe it helps you figure it out (not using Task since it's not available in .NET Framework 3.5). It tries to simulate your long running LoadDevices() method while keeping the UI responsive.
MainWindows.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="120"
Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Click="UpdateButtonClick" Grid.Row="0">Update</Button>
<TextBox Name="TextStatus" Text="" TextWrapping="Wrap" Grid.Row="1"></TextBox>
</Grid>
</Window>
MainWindows.xaml.cs
using System;
using System.Threading;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void UpdateButtonClick(object sender, RoutedEventArgs e)
{
UpdateStatus("Searching for devices, please wait");
var thread = new Thread(LoadDevices);
thread.Start();
}
private void LoadDevices()
{
// Your long running "load devices" implementation goes here
for (int i = 0; i < 15; i++)
{
Dispatcher.BeginInvoke((Action) (() => UpdateStatus(".")));
Thread.Sleep(250);
}
Dispatcher.BeginInvoke((Action)(() => UpdateStatus(" done")));
}
private void UpdateStatus(string status)
{
TextStatus.AppendText(status);
}
}
}
But yeah, you should prefer MVVM, Data binding, Commands, etc. and try to avoid stuffing logic into codebehind.
If you haven't tried delegates, that may be what you are looking for although it seems you may of tried this already. Inside your LoadDevices thread method, you could delegate back to invoke UpdateStatus with whatever text you want while your long method is running.
The other case I see with the wording of your question is something local to the method Update-status is trying to change the text by a call to it. However, it cannot for some reason.
This may be compleatly irrelevant to WPF, but in Forms:
I'm assuming you have your loading thread somewhere else. I don't see you calling a doWork() or equivalent method in the thread though. If you want to update the status as your loading thread loads devices you could do:
private delegate void UpdateStatusDel(string text); //This at your declarations
UpdateStatusHandler = new UpdateStatusDel(UpdateStatus); //To initialize the delegate to / //point to your update textbox function
//say you have
string updateText = "Loading 10% done";
//Then, in your thread you could invoke
[locationOfHandeler].Invoke(UpdateStatusHandler, new object[] { updateText });
Try using the Task library. You will need to download this for .NET 3.5 : http://www.microsoft.com/en-us/download/details.aspx?id=24940
Task task = new Task(new Action(LoadDevices));
task.Start();
There are several ways to do this:
http://dotnetcodr.com/2014/01/01/5-ways-to-start-a-task-in-net-c/