C#异步/等待写入文本框

问题描述:

不用说,我是C#的新手,并且在学习过程中学习了一些基本的知识。我试图了解异步/等待功能。我有一个包含2个文本框和一个按钮的表单。当我单击按钮时,它检查远程计算机的服务状态(在本例中为远程注册表),在检查时它会写入它检查注册表并告诉用户等待,然后在状态停止并获取一些值时启动我寻找并写入第二个文本框C#异步/等待写入文本框

我的代码工作,我能够得到我的价值观但在这段时间GUI冻结我试图实现backgroundworker,线程和异步/等待没有我试图做任务,并不能得到它的工作

这里或其他网站上显示的所有示例只是返回int而我试图返回一个字符串,写入状态和我想要的值textboxes。我试图将它转换为字符串没有成功。

长话短说,对于这种类型的过程,什么是更好的方法?有人能告诉我怎么评论一路上有什么事情,所以我理解得更好?我想学习如何做到这一点,但同时学习为什么,并学习编写更干净的代码。

谢谢大家提前抽出时间。

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); 

      } 
} 
+0

后台工作人员应该为此工作,这是为那些设计的工作。后台工作人员遇到了什么问题? –

+0

嗨艾瑞克,感谢您的快速回复。我用背景工具看到的例子都带有进度条,它向标签结果报告一个整数。我只是无法实现它如何将字符串状态报告返回到文本框。我得到的错误就像你无法从另一种控制类型的东西访问文本框。 – Besiktas

+1

您*说*您无法获得“异步”解决方案,但您完全没有显示任何异步代码。 – Servy

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

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

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分离 - 它只知道它可以报告进度字符串并返回字符串结果。

+0

非常感谢Stephen。所以'var osVersion = await Task.Run(()=> CheckStatus()); “我给了什么论据?是否有更快或更好的servicecontroller“更新”版本?还有“进步?”意味着“如果进步”再次感谢。这真的解释了很多。害怕我多少我不知道寿。 – Besiktas

+0

你将进度传递给它。 'progress?.Report(..);'是if(progress!= null)progress.Report(..)的简称;' –

+0

再次感谢。是的,我补充说,它的工作。 :) 非常感谢。 – Besiktas

我认为你仍然可以使用backgroundworker让你的文本框显示消息而不会冻结GUI。从这个问题Here关于如何从backgroundworker返回一个对象的参考。

+0

BGW用于长时间运行CPU绑定工作,而不是用于IO绑定工作。另外,没有真正的理由使用它,而不是基于TPL的解决方案,这正是OP说他们正在寻找的解决方案。 – Servy

这可以很容易地使用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..."; 
    } 
} 

当点击该按钮时,长时间运行在“大一”“计算”是started'asynchrounously。在运行时,用户界面仍然响应。 当DOIT返回返回其结果(在本例的3秒的假延迟之后)的“点击”事件处理程序继续...

备注:

  • 这个例子使用后面的代码为简单起见。该技术也可以使用MVVM模式(异步操作从命令开始)运行。

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

  • 在现实世界的应用程序中,异步'DoIt'方法可能会位于后端“模型类”中。

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