Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF App, run async task before opening Window

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.

like image 960
Dan Avatar asked Jan 28 '23 08:01

Dan


1 Answers

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>
like image 60
GregorMohorko Avatar answered Feb 02 '23 10:02

GregorMohorko