首页 > 解决方案 > 这是在 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...");
  }
  }

标签: c#wpf

解决方案


当涉及到 UI 控件时,WPF 中有一个简单的规则:

所有更改都必须发生在 UI 线程上。

通过运行后台工作程序、线程,或者Task您正在离开 UI 线程(这是重点,因此您可以避免阻塞它并使您的程序无响应)。因此,如果您需要在任务中运行 UI 逻辑,则需要编组回 UI 线程。这就是它的Dispatcher作用。

也就是说,由于工作方式,您几乎不必在实践中执行此操作(至少在使用时Taskasync/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


推荐阅读