【发布时间】:2021-02-18 11:12:25
【问题描述】:
我正在构建一个简单的 WPF 应用程序,并且在应用程序的许多地方,我都需要进行长时间的操作,在此期间我想更新 UI,使其看起来不像应用程序崩溃。 例如,我有一个登录按钮,当我按下它时,除了验证所有凭据之外,它还会加载该特定用户数据的二进制数据库。对于拥有大量数据的用户,可能需要大约 3-5 秒,对于另一个可能是即时的。
我想要的是能够在完成 Button Click 事件之前更新其他元素,并且我希望按照代码的确切顺序进行。
这里是登录按钮点击事件:
private void btnLogin_Click(object sender, RoutedEventArgs e) {
var username = txtUsername.Text;
var password = pswBox.Password;
lblLoginStatus.Visibility = Visibility.Visible;
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) {
lblLoginStatus.Content = "Both username and password cannot be empty!";
} else {
prgLogin.Visibility = Visibility.Visible; //Progress bar that should be visible while the long operation is running.
var login = Adb.Login(username, password); //Long operation
prgLogin.Visibility = Visibility.Collapsed; //Upon completion of the operation, hide the progress bar.
if (!login) {
lblLoginStatus.Content = "Wrong Credentials!";
} else { //This basically exits the login page/window
LoginPage.Visibility = Visibility.Collapsed;
GeneralPage.Visibility = Visibility.Visible;
}
}
}
如您所见,我没有看任何花哨的动画甚至进度报告。进度条是不确定的,我设置了它的样式,所以它只是以可见性触发器开始和结束。
我的问题是,与我的逻辑相反,在按下按钮后,代码实际上按照与编写时相同的顺序运行,它看起来只是卡住了,在操作结束之前没有更新其他元素,此时登录窗口隐藏起来,让我所有努力创建一个令人愉快的 UI 变得毫无价值。
我尝试过使用:
Dispatcher.CurrentDispatcher.Invoke(/*Whatever element update I want*/);
但是没用,结果还是一样……
我在网上看到了一些类似问题的复杂而冗长的解决方案,我尝试实施它们,即使只是为了测试,我现在搞砸了大约 4 个小时,但无济于事。
就像上面一样,我的应用程序的其余部分充满了以下部分:
progressBar.Visibility = Visibility.Visible;
var operation = AnyLongOperation()
prgLogin.Visibility = Visibility.Collapsed;
因此,我正在寻找一种能够适应整个应用程序的简单解决方案。
【问题讨论】:
-
从 UI 线程调用
Adb.Login(username, password)(如您在此处所做的那样)将阻塞该线程并因此阻塞 UI。使 Login 方法异步并等待它,或者将其包装在等待的任务中。 -
@Clemens 我试图将它包装在一个任务中,然后使用 Task.WaitAll(Task) 但它仍然卡住了......我认为问题是我需要在事件退出验证,如果我使用异步等待,它在等待时会继续所有其他 UI 操作?
-
是的。使处理程序方法异步,即
private async void btnLogin_Click(object sender, RoutedEventArgs e),然后调用var login = await Task.Run(() => Adb.Login(username, password)); -
@Clemens 我认为你应该先使用
var operation = Task.Factory.StartNew(() => Adb.Login(username, password));,然后再使用var login = await operation;,或者它们是否相同? -
你只会在特殊情况下使用Task.Factory.StartNew。如前所述,使用 Task.Run。
标签: c# wpf user-interface dispatcher