C# async/await 写入文本框

C# async/await write to textbox

不用说,我是 C# 的新手,正在做一些基本的事情来学习。我正在尝试了解 async/await 功能。我有一个带有 2 个文本框和一个按钮的表单。当我单击该按钮时,它会检查远程计算机的服务状态(在本例中为远程注册表。在检查时,它会写到它正在检查注册表并告诉用户等待。然后在状态停止时启动并获取一些值我查找并将它们写入第二个文本框。

我的代码有效,而且我能够获取我的值。但是 GUI 在此期间冻结。我尝试实现 backgroundworker、thread 和 Async/Wait 但没有成功。我尝试执行 Task 但无法正常工作。

此处或其他站点上显示的所有示例只是 return int,而我正在尝试 return 一个字符串,该字符串将我想要的状态和值写入文本框。我试图将它转换为字符串但没有成功。

长话短说,对于这种类型的流程,什么是更好的方法?有人可以告诉我如何做并评论一路上发生了什么,以便我更好地理解吗?我想学习如何做,但同时了解原因并学习编写更清晰的代码。

感谢大家提前抽空。

string ComputerName = "Lab01";
    public frmRegChecker()
    {
        InitializeComponent();
    }


    private void Button1_Click(object sender, EventArgs e)
    {
      Check_Status();        

    }

    private void Check_Status()
    {
        TxtBoxstatus.AppendText("Checking Remote Registry service status on computer : " + ComputerName);
        TxtBoxstatus.AppendText(Environment.NewLine);
        TxtBoxstatus.AppendText("Please wait... ");

         ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
            try
            {


                TxtBoxstatus.AppendText("The Remote Registry service status is currently set to : " + sc.Status.ToString());
                TxtBoxstatus.AppendText(Environment.NewLine);


                if (sc.Status == ServiceControllerStatus.Stopped)
                {

                    // Start the service if the current status is stopped.

                    TxtBoxstatus.AppendText("Starting Remote Registry service...");
                    TxtBoxstatus.AppendText(Environment.NewLine);
                    try
                   {
                       // Start the service, and wait until its status is "Running".
                        sc.Start();
                        sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
                        sc.Refresh();
                        sc.WaitForStatus(ServiceControllerStatus.Running);

                        // Display the current service status.
                        TxtBoxstatus.AppendText("The Remote Registry status is now set to:" + sc.Status.ToString());
                       richTextBox1.AppendText(Environment.NewLine);
                        try
                        {
                           var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
                            var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
                            string _OSVersion = (key.GetValue("CurrentVersion")).ToString();
                           richTextBox1.AppendText("OS version is : " + _OSVersion);
                            richTextBox1.AppendText(Environment.NewLine);
                       }
                        catch (InvalidOperationException)
                       {

                            richTextBox1.AppendText("Error getting registry value from" + ComputerName);
                            richTextBox1.AppendText(Environment.NewLine);
                        }
                    }
                    catch (InvalidOperationException)
                    {

                        richTextBox1.AppendText("Could not start the Remote Registry service.");
                        richTextBox1.AppendText(Environment.NewLine);
                    }
                }
                else if (sc.Status == ServiceControllerStatus.Running)
               {
                   try
                   {
                     var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
                     var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
                            string _OSVersion = (key.GetValue("CurrentVersion")).ToString();
                     richTextBox1.AppendText("OS version is : " + _OSVersion);
                     richTextBox1.AppendText(Environment.NewLine);
                    }
                    catch (InvalidOperationException)
                    {

                            richTextBox1.AppendText("Error getting registry value from" + ComputerName);
                            richTextBox1.AppendText(Environment.NewLine);
                    }

                }
           }

          }
          catch
           {                  
             richTextBox1.AppendText("Error getting registry value from " + ComputerName);
             richTextBox1.AppendText(Environment.NewLine);

           }
}

我认为您仍然可以使用 backgroundworker 让您的文本框显示消息而不冻结 GUI。参考这个问题 Here 关于如何 return 来自 backgroundworker 的对象。

这可以使用 async/await 轻松完成。一个例子:

一个简单的 WPF 表单:

<Window x:Class="WpfApp1.MainWindow"
    ...>
  <Grid>
    <Button Name="button" Content="Start" Click="Button_Click"/>
    <TextBox Name="textButton" />
  </Grid>
</Window>

和相关的代码隐藏:

public partial class MainWindow:Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {                        
           textButton.Text = "Running...";
           string result = await DoIt();
           textButton.Text = result;
    }

    private async Task<string> DoIt()
    {
        await Task.Delay(3000);
        return "Finished...";
    }
}

点击按钮时,'DoIt'中的长运行'calculation'异步启动。虽然它是 运行,但 UI 仍然响应。 当 DoIt returns returns 其结果(在本例中经过 3 秒的虚拟延迟)时,'click' 事件处理程序继续...

备注:

  • 为了简单起见,此示例使用代码隐藏。该技术在使用 MVVM 模式(异步操作从命令启动)时同样有效。

  • 'async void' 是一个反模式,但用于遵守代码隐藏中自动生成的事件处理程序。

  • 在现实世界的应用程序中,异步 'DoIt' 方法可能位于后端 'model class'.

  • 该示例假设您只想在 long-运行 操作完成后更新 UI。如果你想要中间更新,有几个选项(不幸的是有点复杂)。

不幸的是,ServiceController 是一个相当过时的 class,并且本身不支持 async/await。如果可能的话,那将是最干净的解决方案。

因此,您可以使用 async/awaitTask.RunTask.Run 在后台线程上执行代码,您可以使用 UI 中的 async/await 来使用该后台操作。这种方法允许以自然方式传播和处理异常, 它允许 return 值也被自然处理。为简单起见,暂时删除文本框更新,第一步如下所示:

private async void Button1_Click(object sender, EventArgs e)
{
  try
  {
    var osVersion = await Task.Run(() => CheckStatus());
    richTextBox1.AppendText("OS version is : " + osVersion);
    richTextBox1.AppendText(Environment.NewLine);
  }
  catch (InvalidOperationException ex)
  {
    richTextBox1.AppendText("Error getting registry value from" + ComputerName);
    richTextBox1.AppendText(ex.ToString());
    richTextBox1.AppendText(Environment.NewLine);
  }
}

private string CheckStatus()
{
  ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
  if (sc.Status == ServiceControllerStatus.Stopped)
  {
    // Start the service if the current status is stopped.
    // Start the service, and wait until its status is "Running".
    sc.Start();
    sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
    sc.Refresh();
    sc.WaitForStatus(ServiceControllerStatus.Running);
  }

  var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
  var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
  return (key.GetValue("CurrentVersion")).ToString();
}

接下来,添加进度更新。有一个内置机制 - IProgress<T>/Progress<T>,它的工作原理如下:

private async void Button1_Click(object sender, EventArgs e)
{
  try
  {
    var progress = new Progress<string>(update =>
    {
      TxtBoxstatus.AppendText(update);
      TxtBoxstatus.AppendText(Environment.NewLine);
    });
    var osVersion = await Task.Run(() => CheckStatus(progress));
    richTextBox1.AppendText("OS version is : " + osVersion);
    richTextBox1.AppendText(Environment.NewLine);
  }
  catch (InvalidOperationException ex)
  {
    richTextBox1.AppendText("Error getting registry value from" + ComputerName);
    richTextBox1.AppendText(ex.ToString());
    richTextBox1.AppendText(Environment.NewLine);
  }
}

private string CheckStatus(IProgres<string> progress)
{
  progress?.Report("Checking Remote Registry service status on computer : " + ComputerName);
  progress?.Report("Please wait... ");

  ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
  progress?.Report("The Remote Registry service status is currently set to : " + sc.Status.ToString());
  if (sc.Status == ServiceControllerStatus.Stopped)
  {
    // Start the service if the current status is stopped.
    progress?.Report("Starting Remote Registry service...");
    // Start the service, and wait until its status is "Running".
    sc.Start();
    sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
    sc.Refresh();
    sc.WaitForStatus(ServiceControllerStatus.Running);
    progress?.Report("The Remote Registry status is now set to:" + sc.Status.ToString());
  }

  var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
  var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
  return (key.GetValue("CurrentVersion")).ToString();
}

注意关注点分离:接触 UI 对象的唯一代码是 UI 事件处理程序。包含实际程序逻辑的 CheckStatus 方法与 UI 分开 - 它所知道的是它可以报告进度字符串和 return 字符串结果。