c# - 这是在 WPF 中使用 Tasking & Dispatcher 的正确方法吗?
问题描述
首先,我想提一下,我对 WPF 有点陌生,几个月前我才开始学习它,我正在使用 atm 后面的代码,稍后会去 MVVM ......所以,我试图为它创建一个简单的程序我的不和谐机器人,但我想在数据库操作正在进行时保持响应,这意味着我的程序在我登录时无法阻止 - 例如 - 登录我的程序。我有办法将其存档,但感觉就像我做错了什么或使用错误的任务。
我使用 await Task.Run(() => { code here }) 但在此任务中,每次我想更新控件时我还需要使用 Dispatcher 并且我想知道我是否以正确的方式执行此操作,每次需要更新控件时在 Task.Run 中使用调度程序是否正常,还是我过度使用/做错了?
目前它按预期工作,但我担心我做得过火或走向错误的方向,因为对我来说,每次都使用调度程序来更新控件似乎很愚蠢,但也许这就是它必须的方式,我是对 WPF 来说还是新手,这也是我在 WPF 中做过的第一个程序
对不起,如果我的英语不是那么好,我会尽力解释这一点
我有一种解决方案,但在 Tasking 中使用 Dispatcher 在我看来不是正确的方法,这是我第一次在 WPF 中这样做,我搜索了很多但看到人们使用后台工作人员或线程,但这似乎仍然阻止我的用户界面
// This button I use to -for this example- login in my program
// I load a ContentPresenter over my program showing its 'loading'
// Then I try login
private async void BtnLogin_Click(object sender, RoutedEventArgs e)
{
cpLoader.Visibility = Visibility.Visible;
await TryLoginAsync();
}
// Then here i do the login with tasking
// But here i also need to use dispatcher when updating controls
// So I wonder if this is done correctly
private async Task TryLoginAsync()
{
var username = txtUsername.Text.Trim();
var password = txtPassword.Password.Trim();
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
await Task.Run(() =>
{
try
{
var programAdmin = _dbAdmins.FindBy(x => x.Username.ToLower() == username.ToLower());
if (programAdmin != null)
{
if (string.Equals(EncryptionManager.Decrypt(BotDBConfiguration.EncryptionKey, programAdmin.Password), password))
{
programAdmin.DateLastLogin = DateTime.Now;
programAdmin.TotalLogins += 1;
_dbAdmins.Update(programAdmin);
Dispatcher.BeginInvoke(new Action(() =>
{
var firstTimeLoginPanel = new UpdatePasswordWindow(programAdmin);
firstTimeLoginPanel.Show();
Close();
}));
}
else
{
Dispatcher.BeginInvoke(new Action(() =>
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Password error", "The given password is incorrect, please try again...");
}));
}
}
else
{
Dispatcher.BeginInvoke(new Action(() =>
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Admin not found", "The given username cannot be found in the admin database, please try again...");
}));
}
}
catch (Exception)
{
Dispatcher.BeginInvoke(new Action(() =>
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Connection error", "Cannot connect to the database, please try again later...");
}));
}
});
}
else
{
cpLoader.Visibility = Visibility.Collapsed;
AlertManager.ShowAlertMessage("Missing information", "Please fill in all the required information...");
}
}
解决方案
当涉及到 UI 控件时,WPF 中有一个简单的规则:
所有更改都必须发生在 UI 线程上。
通过运行后台工作程序、线程,或者Task
您正在离开 UI 线程(这是重点,因此您可以避免阻塞它并使您的程序无响应)。因此,如果您需要在任务中运行 UI 逻辑,则需要编组回 UI 线程。这就是它的Dispatcher
作用。
也就是说,由于工作方式,您几乎不必在实践中执行此操作(至少在使用时Task
)async/await
。当您使用await
它时,它会在完成后自动将控制权返回给调用线程Task
,因此,如果您将代码构造为:
// Do some UI stuff
await dbTask;
// Do more UI stuff
而不是一项艰巨的任务,你会发现你的需求Dispatcher
急剧下降。
正如评论中所指出的,这篇文章提供了很多关于处理异步/等待和任务的超级有用的一般信息:How to run and interact with an async Task from a WPF gui
推荐阅读
- reactstrap - 将 ref 添加到 Reactstrap Jumbotron 会导致错误
- python - 检查目标预期的 dense_2 具有形状(13)但得到具有形状(40)的数组时出错
- javascript - 在路由中模拟 mongoose save() 错误
- python - 在python中使用多维键对存储值
- android - Android:使用 Admob 的 Consent SDK 获取用户的位置
- visual-studio - 如何仅计算 rdlc 报告中的不同值
- java - Windows 和 Unix 机器之间的 Maven 构建结果不同
- abap - 循环 itab where 字段类型表访问字段
- heroku - 如何在应用程序内让 Heroku 免费层上只有工人活着?
- json - 带有json的邮递员Symfony请求