如何创建一个不会停止加载 WebBrowser 的等待计时器?
How to create a waiting timer that does not stop the loading of a WebBrowser?
情况是这样的:
- 我在 VB .NET 项目中有一个 webBrowser
- 我在 Facebook 上登录
- 我必须等待页面/javascript 输出完成加载
我想设置一个不会停止浏览器加载的等待计时器。
这个解决方案似乎不起作用:
Do While WebBrowser1.ReadyState <> WebBrowserReadyState.Complete
Application.DoEvents()
Loop
您应该永远,永远使用Application.DoEvents()
以保持您的UI响应!这样做是 非常糟糕的做法 并且有可能发生很多意想不到的事情,除非它被正确使用(在大多数情况下它不是)。
一个好的经验法则是:任何使用 Application.DoEvents()
的解决方案在 99.9% 的情况下都是 糟糕的解决方案.
有关DoEvents()
为什么如此糟糕的更多信息,请参阅:Keeping your UI Responsive and the Dangers of Application.DoEvents。
我知道这一切可能看起来有点极端,但我真的无法强调这一点。许多人今天仍在使用 DoEvents()
,因为它已通过一些旧论坛和 Stack Overflow 帖子传播,但实际上使用它并不是很好的编程习惯,因为它 几乎总是使用不当。
WebBrowser
控件有一个名为 DocumentCompleted
的事件,而不是使用 DoEvents()
,每次页面(或页面的子部分,例如iframe) 已完全加载。
可以静态订阅事件:
Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
'Do stuff when the page has finished loading.
End If
End Sub
...或动态使用 Lambda Expressions:
Dim DocumentCompletedHandler As WebBrowserDocumentCompletedEventHandler = _
Sub(wsender As Object, we As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
'Do stuff when the page has finished loading.
'If you need to load another page and wait for it, just repeat the exact same code in here (but remember to rename the variables!).
RemoveHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler 'Remove the event handler (if you don't want this to be called twice).
End If
End Sub
AddHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler
WebBrowser1.Navigate("https://www.facebook.com/")
Lambda 表达式解决方案的好处在于,在执行网页自动化等操作时,使用它可以更轻松地添加和删除多个处理程序。
编辑:
如果您尝试执行自动化,我推荐 Lambda 解决方案。这样您就可以更加动态地添加和删除 DocumentCompleted
事件的处理程序,具体取决于您需要做什么。
要等待具有指定文本的元素出现,您可以启动一个计时器来迭代所有元素并在每次滴答时检查它们的 InnerText
。
在下面的示例中,我将计时器的滴答间隔设置为 250 毫秒,因此它将每秒检查该元素大约 4 次。您可以根据需要进行调整,但尽量不要使用太小的延迟,因为这会增加 CPU 使用率。
Dim WithEvents StepTwoWaitingTimer As New Timer With {.Interval = 250}
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim StepOneHandler As WebBrowserDocumentCompletedEventHandler = _
Sub(wsender As Object, we As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
StepTwoWaitingTimer.Start() 'Start waiting for the required element to appear.
RemoveHandler WebBrowser1.DocumentCompleted, StepOneHandler
End If
End Sub
AddHandler WebBrowser1.DocumentCompleted, StepOneHandler
WebBrowser1.Navigate("your URL here")
End Sub
Private Sub StepTwoWaitingTimer_Tick(sender As Object, e As EventArgs) Handles StepTwoWaitingTimer.Tick
'Iterate each element with the "_54nh" class.
For Each Element As HtmlElement In WebBrowser1.Document.GetElementsByClassName("_54nh")
If Element.InnerText = "TEXTINSIDETHETAG" Then
'Element found. Click it and stop the timer.
Element.InvokeMember("click")
StepTwoWaitingTimer.Stop()
Exit For 'Stop the loop.
End If
Next
End Sub
情况是这样的:
- 我在 VB .NET 项目中有一个 webBrowser
- 我在 Facebook 上登录
- 我必须等待页面/javascript 输出完成加载
我想设置一个不会停止浏览器加载的等待计时器。
这个解决方案似乎不起作用:
Do While WebBrowser1.ReadyState <> WebBrowserReadyState.Complete
Application.DoEvents()
Loop
您应该永远,永远使用Application.DoEvents()
以保持您的UI响应!这样做是 非常糟糕的做法 并且有可能发生很多意想不到的事情,除非它被正确使用(在大多数情况下它不是)。
一个好的经验法则是:任何使用 Application.DoEvents()
的解决方案在 99.9% 的情况下都是 糟糕的解决方案.
有关DoEvents()
为什么如此糟糕的更多信息,请参阅:Keeping your UI Responsive and the Dangers of Application.DoEvents。
我知道这一切可能看起来有点极端,但我真的无法强调这一点。许多人今天仍在使用 DoEvents()
,因为它已通过一些旧论坛和 Stack Overflow 帖子传播,但实际上使用它并不是很好的编程习惯,因为它 几乎总是使用不当。
WebBrowser
控件有一个名为 DocumentCompleted
的事件,而不是使用 DoEvents()
,每次页面(或页面的子部分,例如iframe) 已完全加载。
可以静态订阅事件:
Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
'Do stuff when the page has finished loading.
End If
End Sub
...或动态使用 Lambda Expressions:
Dim DocumentCompletedHandler As WebBrowserDocumentCompletedEventHandler = _
Sub(wsender As Object, we As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
'Do stuff when the page has finished loading.
'If you need to load another page and wait for it, just repeat the exact same code in here (but remember to rename the variables!).
RemoveHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler 'Remove the event handler (if you don't want this to be called twice).
End If
End Sub
AddHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler
WebBrowser1.Navigate("https://www.facebook.com/")
Lambda 表达式解决方案的好处在于,在执行网页自动化等操作时,使用它可以更轻松地添加和删除多个处理程序。
编辑:
如果您尝试执行自动化,我推荐 Lambda 解决方案。这样您就可以更加动态地添加和删除 DocumentCompleted
事件的处理程序,具体取决于您需要做什么。
要等待具有指定文本的元素出现,您可以启动一个计时器来迭代所有元素并在每次滴答时检查它们的 InnerText
。
在下面的示例中,我将计时器的滴答间隔设置为 250 毫秒,因此它将每秒检查该元素大约 4 次。您可以根据需要进行调整,但尽量不要使用太小的延迟,因为这会增加 CPU 使用率。
Dim WithEvents StepTwoWaitingTimer As New Timer With {.Interval = 250}
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim StepOneHandler As WebBrowserDocumentCompletedEventHandler = _
Sub(wsender As Object, we As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
StepTwoWaitingTimer.Start() 'Start waiting for the required element to appear.
RemoveHandler WebBrowser1.DocumentCompleted, StepOneHandler
End If
End Sub
AddHandler WebBrowser1.DocumentCompleted, StepOneHandler
WebBrowser1.Navigate("your URL here")
End Sub
Private Sub StepTwoWaitingTimer_Tick(sender As Object, e As EventArgs) Handles StepTwoWaitingTimer.Tick
'Iterate each element with the "_54nh" class.
For Each Element As HtmlElement In WebBrowser1.Document.GetElementsByClassName("_54nh")
If Element.InnerText = "TEXTINSIDETHETAG" Then
'Element found. Click it and stop the timer.
Element.InvokeMember("click")
StepTwoWaitingTimer.Stop()
Exit For 'Stop the loop.
End If
Next
End Sub