【问题标题】:WPF App, run async task before opening WindowWPF App,在打开窗口之前运行异步任务
【发布时间】:2018-09-16 23:20:28
【问题描述】:

我遇到了一个问题,我需要在主窗口打开并显示之前运行异步任务。即。

[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
}

不幸的是,由于启动 Main 函数不是异步的(而且它不可能是因为它是 STAThread)。我不能像上图那样简单地做到这一点。

我尝试了其他几种方法,例如:

        MainWindow window = new MainWindow();

        SplashScreen.Show("Authenticating");
        Authenticate().Wait();
        SplashScreen.Close();

        new Application().Run(window);

但是这个不起作用,因为身份验证代码使用 async/await 发出 Web 请求,因此会自我阻塞,因为它永远无法重新进入同步上下文。

因此,要解决这个问题,您最初会认为只需将其添加到这样的任务中:

        MainWindow window = new MainWindow();

        SplashScreen.Show("Authenticating");
        Task.Run(async () => await Authenticate()).Wait();
        SplashScreen.Close();

        new Application().Run(window);

但这也失败了,因为身份验证方法可能会打开一个消息框,这要求它位于主线程而不是工作线程上。

我的第三次尝试也出现了问题,我尝试了几种变体和组合,包括最小化窗口、从任务栏中删除、将可见性设置为隐藏/折叠等等

        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);

使用这种方法,不会出现死锁,但是在初始化代码完成(即身份验证/验证)之前,窗口已呈现并可访问,这也是不可取的。

显然,上面的代码已被简化,足以显示我的问题。

所以我的问题是如何在窗口对客户端可见之前运行此异步方法。

我知道在完成身份验证之前只在窗口中显示加载屏幕是一种选择,但我的启动画面非常美观,我更愿意使用它。

【问题讨论】:

  • 你为什么不在应用程序的 OnStartup 事件中做这一切?在显示主窗口之前。
  • 因为是异步调用,所以在到达 Web 请求部分时,它会返回给事件调用者,并且应用程序启动将在未完成身份验证的情况下继续。
  • 其实不是这样的。我会发布一个答案,给我一分钟。

标签: c# wpf async-await


【解决方案1】:

您可以在应用程序的OnStartup 方法(在App.xaml.cs 内部)中执行这些操作,而不是在 Main 方法中执行这些操作。只需确保使用async 修饰符标记OnStartup 方法,并从App.xaml 中的应用程序定义中删除StartupUri

例如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();
        }
    }
}

示例App.xaml:(无StartupUri 属性)

<Application x:Class="WpfApp1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
</Application>

【讨论】:

  • 嗯,我会试一试,如果可行的话,我会在星期一接受(工作日快到了)
  • 这绝对有效,我已经这样做了 4 年多了。我已经开发了 10 多个 WPF 应用程序,但我从未在 Main 方法中做过任何事情。如果我是你,我会尽量避免这种情况。
  • 是的,这绝对是个好建议。在您打开第一个窗口之前,尽量减少您所做的事情。否则。如果出现任何问题并且您开始从初始化代码中获取错误,那么您将处于不利地位。尤其是当您的应用在用户中广泛使用时,您不能只是去看看发生了什么。
  • @GregorMohorko 当我尝试在 OnStartUp 中添加异步时,应用程序在点击关闭按钮时并未完全关闭,关闭后仍处于后台进度。它发生在你身上吗?
  • @user38931 不,这应该不是问题。您可能有一些尚未完成的进程(打开的后台线程等),或者只是一个写得不好的异步/等待代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
  • 1970-01-01
相关资源
最近更新 更多