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);
}
}
不幸的是,ServiceController
是一个相当过时的类本身不支持async
/await
。如果可能的话,那将是最干净的解决方案。
所以,你可以做的是使用async
/await
与Task.Run
。 Task.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分离 - 它只知道它可以报告进度字符串并返回字符串结果。
这可以很容易地使用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。如果你想中间更新,有几个选项(不幸的是有点复杂)。
后台工作人员应该为此工作,这是为那些设计的工作。后台工作人员遇到了什么问题? –
嗨艾瑞克,感谢您的快速回复。我用背景工具看到的例子都带有进度条,它向标签结果报告一个整数。我只是无法实现它如何将字符串状态报告返回到文本框。我得到的错误就像你无法从另一种控制类型的东西访问文本框。 – Besiktas
您*说*您无法获得“异步”解决方案,但您完全没有显示任何异步代码。 – Servy