C# 在Windows窗体中使用webbrowser控件处理非UI阻塞
我有一个带有webbrowser控件(此处为wb)的小型windows窗体应用程序,它执行一些任务,如登录门户、搜索项目、更改某些属性/复选框以及提交表单。 完成的每一步都是通过更改标签的文本来宣布的,这样用户就可以看到当前所做的事情 处理与webbrowser控件交互的最佳方式是什么,这样UI就不会被阻塞 我这样试过:C# 在Windows窗体中使用webbrowser控件处理非UI阻塞,c#,.net,async-await,task,webbrowser-control,C#,.net,Async Await,Task,Webbrowser Control,我有一个带有webbrowser控件(此处为wb)的小型windows窗体应用程序,它执行一些任务,如登录门户、搜索项目、更改某些属性/复选框以及提交表单。 完成的每一步都是通过更改标签的文本来宣布的,这样用户就可以看到当前所做的事情 处理与webbrowser控件交互的最佳方式是什么,这样UI就不会被阻塞 我这样试过: private async void buttonStart_Click(object sender, EventArgs e) { //Here it is stil
private async void buttonStart_Click(object sender, EventArgs e)
{
//Here it is still possible to access the UI thread
buttonStart.Enabled = false;
//do the background work
await Task.Run(() =>
{
Login();
CallProject();
TriggerTask1();
TriggerTask2();
});
//back on UI thread
buttonStart.Enabled = false;
}
private void Login()
{
//may not access labelProgress from this thread
//labelProgress.Text = "logging in";
Invoke(new Action(() =>
{
//can access labelProgress here
labelProgress.Text = "logging in";
//can access wb here
wb.Navigate(Settings.Default.LoginUrl);
WebBrowserTools.Wait(wb);
wb.Document.GetElementById("username").InnerText = Settings.Default.LoginUsername;
wb.Document.GetElementById("password").InnerText = Settings.Default.LoginPassword;
wb.Document.Forms[0].InvokeMember("submit");
WebBrowserTools.Wait(wb);
labelProgress.Text = "logged in successfully";
}));
}
对webbrowser或任何其他ui控件(如labelProgress)的每次访问都必须包装在Invoke(新操作(()=>{})中代码>。
有没有更好的方法(使用.NET Framework>=4.5的现代版本)?您使用的是异步等待,而不是预期的方式
。在中间搜索某处,等待。< /P>
他把异步等待比作厨师必须做早餐。在他把水壶烧开水沏茶后,他可以决定什么都不做,直到水烧开,然后他沏茶并开始烤面包。在面包烤好之前不要做任何事
如果厨师在等待另一个过程结束时,在等待第一件事情完成之前,开始做其他事情,而不是什么都不做,那么速度会快得多:
Start boiling water
Start toasting bread
wait until the water boils
use the boiling water to start making tea
...
您将看到async wait通常出现在线程命令某个进程执行线程自己无法执行的操作时:从数据库获取数据、将数据写入文件、从internet获取数据:线程只能等待的操作
你正确地开始了你的程序。你的厨师做了一些准备工作,但是他决定他应该雇佣另一个厨师为他做这项工作,而你的厨师什么也不做,只是等到另一个厨师完成工作。你的厨师为什么不这么做
更典型的异步等待如下所示:
async void buttonStart_Click(object sender, EventArgs e)
{
buttonStart.Enabled = false;
await LoginAsync();
await CallProjectAsync();
await TriggerTask1Async();
await TriggerTask2Async();
buttonStart.Enabled = false;
}
通常,如果您使用支持async Wait的对象,这将起作用。对于每个异步函数,您都知道在某个地方有一个对可等待的异步函数的调用。事实上,如果您在不等待任何调用的情况下声明函数为async,编译器会抱怨
唉,有时候你的厨师在做其他事情时,不能依靠一个过程来继续。这是因为WebBrowser类不像WebClient类那样支持异步等待
这类似于当你的厨师不得不做一些繁重的处理时,你仍然想保持你的程序异步等待,比如切西红柿:水壶会自动煮沸水,你需要另一个厨师来切西红柿
在异步等待场景中,通常会使用Task.Run。在您的情况下,这是例如在您的登录功能
async void LoginAsync()
{
// still main thread. Notify the operator:
labelProgress.Text = "logging in";
// we start something that takes some time that this cook can't spend:
// let another cook do the logging in
await Task.Run( () =>
{
wb.Navigate(Settings.Default.LoginUrl);
WebBrowserTools.Wait(wb);
wb.Document.GetElementById("username").InnerText = Settings.Default.LoginUsername;
wb.Document.GetElementById("password").InnerText = Settings.Default.LoginPassword;
wb.Document.Forms[0].InvokeMember("submit");
WebBrowserTools.Wait(wb);
}
// back in main thread:
labelProgress.Text = "logged in successfully";
}
所以诀窍是:尽可能多地使用异步等待准备好的类。只有当你的厨师不得不做一些冗长的事情,但他想自由地做其他事情时,才可以执行Task.Run()。等待你的厨师需要确定另一个厨师完成了任务的时候。当你有其他事情要做时,不要等待。让您的厨师来处理用户界面。您正在以一种非预期的方式使用async await
。在中间搜索某处,等待。< /P>
他把异步等待比作厨师必须做早餐。在他把水壶烧开水沏茶后,他可以决定什么都不做,直到水烧开,然后他沏茶并开始烤面包。在面包烤好之前不要做任何事
如果厨师在等待另一个过程结束时,在等待第一件事情完成之前,开始做其他事情,而不是什么都不做,那么速度会快得多:
Start boiling water
Start toasting bread
wait until the water boils
use the boiling water to start making tea
...
您将看到async wait通常出现在线程命令某个进程执行线程自己无法执行的操作时:从数据库获取数据、将数据写入文件、从internet获取数据:线程只能等待的操作
你正确地开始了你的程序。你的厨师做了一些准备工作,但是他决定他应该雇佣另一个厨师为他做这项工作,而你的厨师什么也不做,只是等到另一个厨师完成工作。你的厨师为什么不这么做
更典型的异步等待如下所示:
async void buttonStart_Click(object sender, EventArgs e)
{
buttonStart.Enabled = false;
await LoginAsync();
await CallProjectAsync();
await TriggerTask1Async();
await TriggerTask2Async();
buttonStart.Enabled = false;
}
通常,如果您使用支持async Wait的对象,这将起作用。对于每个异步函数,您都知道在某个地方有一个对可等待的异步函数的调用。事实上,如果您在不等待任何调用的情况下声明函数为async,编译器会抱怨
唉,有时候你的厨师在做其他事情时,不能依靠一个过程来继续。这是因为WebBrowser类不像WebClient类那样支持异步等待
这类似于当你的厨师不得不做一些繁重的处理时,你仍然想保持你的程序异步等待,比如切西红柿:水壶会自动煮沸水,你需要另一个厨师来切西红柿
在异步等待场景中,通常会使用Task.Run。在您的情况下,这是例如在您的登录功能
async void LoginAsync()
{
// still main thread. Notify the operator:
labelProgress.Text = "logging in";
// we start something that takes some time that this cook can't spend:
// let another cook do the logging in
await Task.Run( () =>
{
wb.Navigate(Settings.Default.LoginUrl);
WebBrowserTools.Wait(wb);
wb.Document.GetElementById("username").InnerText = Settings.Default.LoginUsername;
wb.Document.GetElementById("password").InnerText = Settings.Default.LoginPassword;
wb.Document.Forms[0].InvokeMember("submit");
WebBrowserTools.Wait(wb);
}
// back in main thread:
labelProgress.Text = "logged in successfully";
}
所以诀窍是:尽可能多地使用异步等待准备好的类。只有当你的厨师不得不做一些冗长的事情,但他想自由地做其他事情时,才可以执行Task.Run()。等待你的厨师需要确定另一个厨师完成了任务的时候。当你有其他事情要做时,不要等待。让你的厨师来处理用户界面。调用UI线程上的调用,这将反过来阻止UI。你刚刚想出了一个绝妙的方法。只调用
UI部件并异步调用
WebBrowserTools怎么样?从任务中等待?那是