You are getting the events in the correct order. The async void
with the await
in the event handlers is confusing the issue. From the perspective of the Form
class, when it calls your Load
event handler, the event handler method returns when the await in SomeMethod
is hit. When the SomeMethod
task completes, the rest of the code in the Load event handler is executed as a continuation back on the UI thread - though it looks like it is still in the Load event handler because that is where YOUR code is located, that's not what the compiler emits. A bit confusing, for sure. Jon Skeet's book C# in Depth: Fourth Edition contains perhaps one of the best explanations of this I have seen.
Also, avoid the use of async void
methods if at all possible, as throwing an exception can crash your process. To solve both of these problems, and add some clarity, I would use the Load event to start the task, then attach a continuation to it in Shown. This ensures that the code you want to run when your async stuff in Load completes and Shown happens.
private Task _loadTask = Task.CompletedTask;
private Stopwatch _sw = Stopwatch.StartNew();
private void Form1_Load(object sender, EventArgs e)
{
Trace.TraceInformation("[{0}] {1} Form1_Load - Begin", Thread.CurrentThread.ManagedThreadId, _sw.ElapsedMilliseconds);
_loadTask = DoAsyncLoadStuff();
Trace.TraceInformation("[{0}] {1} Form1_Load - End", Thread.CurrentThread.ManagedThreadId, _sw.ElapsedMilliseconds);
}
private void Form1_Shown(object sender, EventArgs e)
{
Trace.TraceInformation("[{0}] {1} Form1_Shown - Begin", Thread.CurrentThread.ManagedThreadId, _sw.ElapsedMilliseconds);
_loadTask.ContinueWith(DoStuffAfterShownAndAsyncLoadStuff, null, TaskScheduler.FromCurrentSynchronizationContext());
Trace.TraceInformation("[{0}] {1} Form1_Shown - End", Thread.CurrentThread.ManagedThreadId, _sw.ElapsedMilliseconds);
}
private void DoStuffAfterShownAndAsyncLoadStuff(Task asyncLoadTask, object context)
{
Trace.TraceInformation("[{0}] {1} DoStuffAfterShownAndAsyncLoadStuff. Task was {2}", Thread.CurrentThread.ManagedThreadId,
_sw.ElapsedMilliseconds, asyncLoadTask.Status);
}
private async Task DoAsyncLoadStuff()
{
await Task.Delay(5000).ConfigureAwait(false);
Trace.TraceInformation("[{0}] {1} DoAsyncLoadStuff - Returning", Thread.CurrentThread.ManagedThreadId, _sw.ElapsedMilliseconds);
//throw new NotImplementedException();
}
The sample above will output to the Output window in Visual Studio the thread id and elapsed milliseconds. Tweak the Delay time and uncomment throwing the exception. Note that DoStuffAfterShownAndAsyncLoadStuff
will always execute on the UI thread.