I am running into an issue where I need to run an async task before my main window opens up and displays. I.E.
[STAThread]
static void Main(string[] args)
MainWindow window = new MainWindow();
SplashScreen.Show("Authenticating");
await Authenticate(); // Need something similar to this....
SplashScreen.Close();
new Application().Run(window);
}
static async Task Authenticate()
{
// Load Local Auth Data
var result = await Authenticator.Authenticate(); // Validates google token with webservice
if (result.GoogleReauthRequired)
{
if (MessageBox.Show(......) == MessageBoxResult.Yes)
{
Google.Reauthenticate();// Opens web browser for account to be logged into using OAuth
result = await Authenticator.Authenticate();
}
}
// Initialize state
}
Unfortuanently, due to the startup Main function not being async(And it can't be because it is STAThread). I cannot do it as simply as pictured above.
I have tried a couple other approaches such as:
MainWindow window = new MainWindow();
SplashScreen.Show("Authenticating");
Authenticate().Wait();
SplashScreen.Close();
new Application().Run(window);
But this one didn't work because the authenticate code makes a web request using async/await and therefore will be self blocking as it can never re-enter the synchronization context.
So to solve that, you would initially think just add it to a task as such:
MainWindow window = new MainWindow();
SplashScreen.Show("Authenticating");
Task.Run(async () => await Authenticate()).Wait();
SplashScreen.Close();
new Application().Run(window);
But this too fails due to the Authentication method potentially opening a message box, which requires it to be on the main thread rather than a worker thread.
My third attempt also resulted in problems, I tried several variations and combinations of minimizing the window, removing from taskbar, setting visibility to hidden/collapsed, and more
MainWindow window = new MainWindow();
// Visibility attempts
window.Loaded += async (a, b) =>
{
await Authentication();
// Undoing the above visibility attempts
SplashScreen.Close();
};
SplashScreen.Show("Authenticating");
new Application().Run(window);
With this approach, there is no deadlock, but the window is rendered and accessible before the initialization code is finished(Namely authentication/validation) Which is also undesirable.
Obviously the above code has been simplified to be enough to show my issues.
So my question is how I can run this async method before the window becomes visible to the client.
I am aware that it is an option to just display a loading screen in the window until the authentication is done, but my Splash Screen is very aesthetically pleasing and I would much rather use it.
Instead of doing these things in the Main method, you can do it in the OnStartup
method of your Application (inside App.xaml.cs
). Just make sure to mark the OnStartup
method with the async
modifier and to remove the StartupUri
from your Application definition in the App.xaml
.
Example App.xaml.cs
:
namespace WpfApp1
{
public partial class App : Application
{
protected override async void OnStartup(StartupEventArgs e)
{
SplashScreen.Show("Authenticating");
await Authenticate();
SplashScreen.Close();
MainWindow = new MainWindow();
MainWindow.Show();
}
}
}
Example App.xaml
: (no StartupUri
attribute)
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
</Application>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With