【发布时间】: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