I've been trying for hours to wait for an async method to complete before continue with the application. If I debug everything works great because I'm actually waiting for the async method to complete but without debugging the code executes while calling the method and nothing works.
Here is my code:
private async void MenuPage_Loaded(object sender, RoutedEventArgs e)
{
MenuItems = await SetItemSource();
ItemSource = new ObservableCollection<AlphaKeyGroup<Menu>>((AlphaKeyGroup<Menu>.CreateGroups(MenuItems, CultureInfo.CurrentUICulture, s => s.MenuName, true)));
((CollectionViewSource)Resources["MenuGroups"]).Source = ItemSource;
}
private async Task<ObservableCollection<Menu>> SetItemSource()
{
return await MyWinService.GetMenuEntriesAsync();
}
Remove the async/await in the SetItemSource method. You are returning an awaitable task that is telling MenuPage_Loaded to continue at that point.
Async is not required in the second method as there is no continuation.
Okay, I did this:
private async void MenuPage_Loaded(object sender, RoutedEventArgs e)
{
var MenuItemsTask = MyWinService.GetMenuEntriesAsync();
MenuItems = await MenuItemsTask;
ItemSource = new ObservableCollection<AlphaKeyGroup<Menu>>((AlphaKeyGroup<Menu>.CreateGroups(MenuItems, CultureInfo.CurrentUICulture, s => s.MenuName, true)));
((CollectionViewSource)Resources["MenuGroups"]).Source = ItemSource;
}
I swear it was not working but I republished my ServiceReference (without changing anything) and suddenly it works, I mean, I don't know what is happening, I hate to not know what happened haha.
Thanks for the help.
Related
I have a problem in a Windows Forms application where I use PerformClick to call an async event handler. It seems that the event handler doesn't await but just returns immediately. I have created this simple application to show the problem (it's just a form with 3 buttons, easy to test yourself):
string message = "Not Started";
private async void button1_Click(object sender, EventArgs e)
{
await MyMethodAsync();
}
private void button2_Click(object sender, EventArgs e)
{
button1.PerformClick();
MessageBox.Show(message);
}
private void button3_Click(object sender, EventArgs e)
{
MessageBox.Show(message);
}
private async Task MyMethodAsync()
{
message = "Started";
await Task.Delay(2000);
message = "Finished";
}
The interesting problem here is, what do you think message shows, when I click Button2?
Surprisingly, it shows "Started", not "Finished", as you would expect. In other Words, it doesn't await MyMethod(), it just starts the Task, then continues.
Edit:
In this simple code I can make it Work by calling await Method() directly from Button2 event handler, like this:
private async void button2_Click(object sender, EventArgs e)
{
await MyMethodAsync();
MessageBox.Show(message);
}
Now, it waits 2 seconds and displays 'Finished'.
What is going on here? Why doesn't it work when using PerformClick?
Conclusion:
Ok, now I get it, the conclusion is:
Never call PerformClick if the eventhandler is async. It will not await!
Never call an async eventhandler directly. It will not await!
What's left is the lack of documentation on this:
Button.PerformClick should have a Warning on the doc page:
Button.PerformClick
"Calling PerformClick will not await async eventhandlers."
Calling an async void method (or eventhandler) should give a compiler Warning: "You're calling an async void method, it will not be awaited!"
You seem to have some misconceptions about how async/await and/or PerformClick() work. To illustrate the problem, consider the following code:
Note: the compiler will give us a warning but let's ignore that for the sake of testing.
private async Task MyMethodAsync()
{
await Task.Delay(2000);
message = "Finished"; // The execution of this line will be delayed by 2 seconds.
}
private void button2_Click(object sender, EventArgs e)
{
message = "Started";
MyMethodAsync(); // Since this method is not awaited,
MessageBox.Show(message); // the execution of this line will NOT be delayed.
}
Now, what do you expect the MessageBox to show? You'd probably say "started".1 Why? Because we didn't await the MyMethodAsync() method; the code inside that method runs asynchronously but we didn't wait for it to complete, we just continued to the next line where the value of message isn't yet changed.
If you understand that behavior so far, the rest should be easy. So, let's change the above code a little bit:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Delay(2000);
message = "Finished"; // The execution of this line will be delayed by 2 seconds.
}
private void button2_Click(object sender, EventArgs e)
{
message = "Started";
button1_Click(null, null); // Since this "method" is not awaited,
MessageBox.Show(message); // the execution of this line will NOT be delayed.
}
Now, all I did was that I moved the code that was inside the async method MyMethodAsync() into the async event handler button1_Click, and then I called that event handler using button1_Click(null, null). Is there a difference between this and the first code? No, it's essentially the same thing; in both cases, I called an async method without awaiting it.2
If you agree with me so far, you probably already understand why your code doesn't work as expected. The code above (in the second case) is nearly identical to yours. The difference is that I used button1_Click(null, null) instead of button1.PerfomClick(), which essentially does the same thing.3
The solution:
If you want to wait for the code in button1_Click to be finished, you need to move everything inside button1_Click (as long as it's asynchronous code) into an async method and then await it in both button1_Click and button2_Click. This is exactly what you did in your "Edit" section but be aware that button2_Click will need to have an async signature as well.
1 If you thought the answer was something else, then you might want to check this article which explains the warning.
2 The only difference is that in the first case, we could solve the problem by awaiting the method, however, in the second case, we can't do that because the "method" is not awaitable because the return type is void even though it has an async signature.
3Actually, there are some differences between the two (e.g., the validation logic in PerformClick()), but those differences don't affect the end result in our current situation.
I'm working on a plugin for .Net WPF application which uses the frame pushing technique to achieve 'responsible' UI. Each long-running task in its SDK calls the code:
public static void Wait(Task task)
{
DispatcherFrame nestedFrame = new DispatcherFrame();
task.ContinueWith(delegate { return nestedFrame.Continue = false; });
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
So the typical SDK's method can be emulated by the following code:
private void SdkMethod()
{
var t = Task.Factory.StartNew(() =>
{
Thread.Sleep(1500);
});
Wait(t);
}
It causes a situation when I can not do two synchronous calls to the SDK's methods from the UI thread:
private void OnClick(object sender, RoutedEventArgs e)
{
this.sdk.SdkMethod();
this.sdk.SdkMethod();
}
Is there any way to synchronize calls in such a situation? I cannot modify SDK, only my own calls to it.
If I understood correctly from reading the post and comments, you want to call SdkMethod and have it act synchronously. It's being called without letting you know when it is done.
If it is a void method and it isn't a task, you aren't going to get anything in return. I tried to listen to the Dispatcher Hooks for completion, but that was not helpful.
I'd create an aysnc Task method that has a Task.Delay then await it between calls. That way you aren't interrupting the user, but you still force the call to be paused until you call the second method.
private void OnClick(object sender, RoutedEventArgs e)
{
this.sdk.SdkMethod();
await DelayAsync(3).ConfigureAwait(false);
this.sdk.SdkMethod();
}
private async Task DelayAsync(int seconds)
{
await Task.Delay(seconds * 1000).ConfigureAwait(false);
}
I hope this helps.
I have a problem with application freezes for a few seconds.
I loading data from XML file and deserialize to MyList.
public List<My20FieldsDataRecord> MyList;
...
void ShowDataInThread()
{
MyGrid.DataContext = MyList;
}
public void ShowDane(bool inThread)
{
if (inThread)
{
Thread thr = new Thread(ShowDataInThread);
thr.Start();
}
else
{
ShowDataInThread();
}
}
if inThread = false everything work fine, but application not responding for a 2-3 seconds.
When inThread = true application crash.
I want do this in thread, but i was not able to understand that how it works from examples on internet. I'll be very grateful for your help, becouse i have no idea how to do that.
Since Microsoft introduced the async / wait approach for .NET Framework programming in .NET 4.5, the code for async methods is a lot.
You can not find any async async with as example as such as type:
private async void button1_Click(object sender, EventArgs e)
{
string result = await AnMethodAsync();
textBox1.Text += result;
}
private Task<string> AnMethodAsync()
{
//Do somethine async
}
And you think this is done, the function will run async do not have to worry about hanging thead anymore, too strong.
But the problem is not so simple.
Now try to put in the AnMethodAsync () function the following code:
Thread.Sleep(5000);
return Task.FromResult("HoanHT");
Run the code above and when you press button1, the UI will hang stiff for 5s.
What the hell, I have applied async / await properly that the UI is still hanging.
After a brief look at the problem: In the AnMethodAsync function does not create any other task on the other thread. The consequence is that asyn away, but it still runs on UI thread -> UI freeze.
Then fix it, there are two ways:
Method 1: Create a new task and executable:
return Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
return "HoanHT";
});
Method 2: Use Task.Delay () instead of Thread.Sleep ()
private async Task<string> AnMethodAsync()
{
await Task.Delay(5000);
return "HoanHT";
}
This is the way I usually do with the asynchronous problem, plus one more way is to use ConfigureAwait () for the Task but will cause minor problems.
Here is a way that I've found to load data for a datagrid in the background while not blocking your UI.
First, create a lock object, and enable collection synchronization, then actually load the data on a background thread using Task.Run():
private readonly object _myDataLock = new object();
private FastObservableCollection<My20FieldsDataRecord> MyList = new FastObservableCollection<My20FieldsDataRecord>();
private CollectionViewSource MyListCollectionView = new CollectionViewSource();
public MyViewModelConstructor() : base()
{
// Other ctor code
// ...
// assign the data source of the collection views
MyListCollectionView.Source = MyList;
// Setup synchronization
BindingOperations.EnableCollectionSynchronization(MyList, _myDataLock);
}
private async void LoadMyList()
{
// load the list
await Task.Run(async () =>
{
MyList.ReplaceAll(await MyRepository.LoadMyList());
}
);
}
Then in your repository you could write:
public virtual async Task<IList<My20FieldsDataRecord>> LoadMyList()
{
var results = await this.DataContext.TwentyFieldDataRecords
.OrderByDescending(x => x.MyDate).ToListAsync().ConfigureAwait(false);
return results;
}
Then you could bind in your associated view like this:
<controls:DataGrid Name="MyListDataGrid" Grid.Row="1"
....
ItemsSource="{Binding MyListCollectionView.View}"
... >
For details, please see:
https://blog.stephencleary.com/2014/04/a-tour-of-task-part-0-overview.html
http://blog.stephencleary.com/2012/02/async-and-await.html#avoiding-context
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7);k(DevLang-csharp)&rd=true
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I have a task that runs in the form_load event of a usercontrol in winforms:
private void ucDeviceInsert_Load(object sender, EventArgs e)
{
System.Threading.Tasks.Task getTBox = System.Threading.Tasks.Task.Run(async () =>
{
await AVeryLongRunningProccess();
});
pbImage.Image = Properties.Resources.Remove;
getTBox.Wait();
pbImage.Image = Properties.Resources.Insert;
btnNext.Visible = true;
tmrDeviceInsert.Enabled = true;
tmrDeviceInsert.Start();
}
private void tmrDeviceInsert_Tick(object sender, EventArgs e)
{
Next();
}
I change the image of the picture box to inform the user the progress of the long running process. That part works fine, however the button doesn't show, and the timer never starts. I've stepped through the code, and I can confirm that it is running without any problems, which makes this even more baffling. Any ideas what would be causing this issue?
Task.Run is for pushing CPU-intensive work off the UI thread. Since you're calling an asynchronous method, I suspect it's not CPU-intensive.
So, you can just use async and await:
private async void ucDeviceInsert_Load(object sender, EventArgs e)
{
pbImage.Image = Properties.Resources.Remove;
await AVeryLongRunningProccess();
pbImage.Image = Properties.Resources.Insert;
btnNext.Visible = true;
tmrDeviceInsert.Enabled = true;
tmrDeviceInsert.Start();
}
Note that at the await, the UI is shown and the user can interact with it (that's the point).
getTBox.Wait() is going to try to complete that task synchronously. Therefore, the rest of the code after it won't happen until after the task completes.
I'd think you don't want your task to run synchronously at all, but rather handle its completion asynchronously, something like this:
getTBox.ContinueWith(() => updateStatusInUI());
i was doing some processor heavy task and every time i start executing that command my winform freezes than i cant even move it around until the task is completed. i used the same procedure from microsoft but nothing seem to be changed.
my working environment is visual studio 2012 with .net 4.5
private async void button2_Click(object sender, EventArgs e)
{
Task<string> task = OCRengine();
rtTextArea.Text = await task;
}
private async Task<string> OCRengine()
{
using (TesseractEngine tess = new TesseractEngine(
"tessdata", "dic", EngineMode.TesseractOnly))
{
Page p = tess.Process(Pix.LoadFromFile(files[0]));
return p.GetText();
}
}
Yes, you're still doing all the work on the UI thread. Using async isn't going to automatically offload the work onto different threads. You could do this though:
private async void button2_Click(object sender, EventArgs e)
{
string file = files[0];
Task<string> task = Task.Run(() => ProcessFile(file));
rtTextArea.Text = await task;
}
private string ProcessFile(string file)
{
using (TesseractEngine tess = new TesseractEngine("tessdata", "dic",
EngineMode.TesseractOnly))
{
Page p = tess.Process(Pix.LoadFromFile(file));
return p.GetText();
}
}
The use of Task.Run will mean that ProcessFile (the heavy piece of work) is executed on a different thread.
You can also do this by starting your task in new thread.
Just use Thread.Start or Thread. ParameterizedThreadStart
See these for your reference:
http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx
Start thread with parameters
You could use BackgroundWorker component.