WebBrowser DocumentComplete Not Loaded on First Click VB.Net - vb.net

I have a problem when click button 1st time, webbrowser.DocumentText always not loaded, but on 2nd or 3rd time clickings the documentText is ALWAYS loaded.
Glad if someone can advice.
my code as below:
Private Property pageready As Boolean = False
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click
WebBrowser1.Navigate(url)
WaitForPageLoad()
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox ("Document Loaded")
End Sub
Private Sub WaitForPageLoad()
AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
While Not pageready
Application.DoEvents()
End While
pageready = False
End Sub
Private Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
pageready = True
RemoveHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
End If
End Sub

Task Delay till the DocumentComplete loaded is the solustion for me.
Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click
WebBrowser1.Navigate(url)
Await ExampleMethodAsync()
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox ("Document Loaded")
End Sub
Async Function ExampleMethodAsync() As Task
' The following line simulates a task-returning asynchronous process.
Await Task.Delay(1750)
End Function

Here are two different ways you can dynamically wait for the WebBrowser to load the entire page:
Extending your Async/Await method to actually wait for the page to load you can use a ManualResetEvent. The MRE functions as a door: When you call Set() you open the door, when you call Reset() you close it, and when you call WaitOne(-1) you wait by the door for it to open (if it's closed).
Private DocumentCompletedResetEvent As New ManualResetEvent(False)
Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click
WebBrowser1.Navigate(url)
Await ExampleMethodAsync()
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox("Document Loaded")
End Sub
Async Function ExampleMethodAsync() As Task
DocumentCompletedResetEvent.Reset() 'Makes sure that the MRE is closed.
DocumentCompletedResetEvent.WaitOne(-1) 'Waits for it to open.
End Function
Private Sub WebBrowser1_DocumentCompleted(sender As System.Object, e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
DocumentCompletedResetEvent.Set() 'Opens the MRE.
End If
End Sub
The second method adds a temporary event handler to the DocumentCompleted event. Using Lambda expressions you may create an in-line method for the event handler.
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
WebBrowser1.Navigate(TextBox1.Text)
'Creates a temporary event handler.
Dim DocumentCompletedHandler As WebBrowserDocumentCompletedEventHandler = _
Sub()
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
RemoveHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler 'Removes the temporary event handler.
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox("Document Loaded")
End If
End Sub
AddHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler 'Adds the temporary event handler.
End Sub

Related

Auto Website Login: SetAttribute not working

I'm creating a program dashboard and one feature is that the user is automatically logged into a website site using stored credentials in the program (no need to open chrome or FF).
In the program, await task delay is working, I see the username and password fields populate before submission (click), but when it tries to submit, the browser, built into the form, acts like the page is empty and no credentials have been entered? I should mention that I can see the username and password entered in the form, but the page acts like nothing has been entered. What am I doing wrong here?
Side note: The button on the site we're connecting to doesn't have an element ID, only a type is shown...hence the workaround for Invokemember("Click")
Any help is appreciated.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Label3.Visible = False
End Sub
Private Function login_thesite() As Task
WebBrowser1.Document.GetElementById("username").SetAttribute("value", "Username")
WebBrowser1.Document.GetElementById("Password").SetAttribute("value", "Password")
Dim allelements As HtmlElementCollection = WebBrowser1.Document.All
For Each webpageelement As HtmlElement In allelements
If webpageelement.GetAttribute("type") = "submit" Then
webpageelement.InvokeMember("click")
End If
Next
End Function
Private Property pageready As Boolean = False
#Region "Page Loading Functions"
Private Sub WaitForPageLoad()
AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
While Not pageready
Application.DoEvents()
End While
pageready = False
End Sub
Private Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
pageready = True
RemoveHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
End If
End Sub
#End Region
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If CheckBox1.Checked = True Then
login_thesite()
WaitForPageLoad()
End If
End Sub
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
TextBox1.Text = ""
TextBox2.Text = ""
Label3.Visible = True
WebBrowser1.Navigate("https://thesite.com/#/login")
WaitForPageLoad()
End Sub
End Class
You don't need any async procedure here. The WebBrowser.DocumentCompleted event is already invoked asynchronously. DoEvents() is equally useless if not disruptive.
You just need to subscribe to the DocumentCompleted event and call the Navigate method to let the WebBrowser load the remote Html resource.
When the HtmlDocument is finally loaded, the WebBrowser will signal its completion setting its state to WebBrowserReadyState.Complete.
About the Html Input Element and Forms:
here, the code is supposing that there's only one Form in that HtmlDocument.
It may be so, but it may be not. A Html Document can have more than one Form and each Frame can have its own Document. IFrames will have one each for sure.
Read the notes in this answer (C# code, but you just need the notes) for more information on how to handle multiple Frames/IFrames
Button1 will wire up the DocumentCompleted event and call Navigate().
When the Document is completed, the code in the event handler will run and perform the LogIn procedure.
The event handler is then removed, since it has complelted its task and you still need to use the WebBrowser for other purposes.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Button1.Enabled = False
WebSiteLogIn()
End Sub
Private Sub WebSiteLogIn()
AddHandler WebBrowser1.DocumentCompleted, AddressOf PageWaiter
WebBrowser1.Navigate("https://thesite.com/#/login")
End Sub
Private Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
WebBrowser1.Document.GetElementById("username").SetAttribute("value", "Username")
WebBrowser1.Document.GetElementById("Password").SetAttribute("value", "Password")
Dim allInputElements = WebBrowser1.Document.Body.All.
Cast(Of HtmlElement).Where(Function(h) h.TagName.Equals("INPUT")).ToList()
For Each element As HtmlElement In allInputElements
If element.GetAttribute("type").ToUpper().Equals("SUBMIT") Then
element.InvokeMember("click")
End If
Next
RemoveHandler WebBrowser1.DocumentCompleted, AddressOf PageWaiter
Button1.Enabled = True
End If
End Sub

Why does backgroundworker lags or freezes for a bit when waiting for page to load on webbrowser?

I'm running a backgroundworker for my webbrowser on a winform, it works and all, but why does the UI lags or freezes for a moment to wait for the page to load when I want it to load in the background so that the UI is fully functional?
Private Property pageready As Boolean = False
Public Sub WaitForPageLoad()
AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
While Not pageready
Application.DoEvents()
End While
pageready = False
End Sub
Public Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
pageready = True
RemoveHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Try
WebBrowser1.Navigate("http://www.mypage.com")
WaitForPageLoad() >>>> Lags and freezes UI for a sec or two to wait for page loading. How can i do this in the background?
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Here i will get my attributes off the webpage ect....
End Sub

Is there a way to run a webbrowser on a form in the background? [duplicate]

I'm running a backgroundworker for my webbrowser on a winform, it works and all, but why does the UI lags or freezes for a moment to wait for the page to load when I want it to load in the background so that the UI is fully functional?
Private Property pageready As Boolean = False
Public Sub WaitForPageLoad()
AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
While Not pageready
Application.DoEvents()
End While
pageready = False
End Sub
Public Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
pageready = True
RemoveHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Try
WebBrowser1.Navigate("http://www.mypage.com")
WaitForPageLoad() >>>> Lags and freezes UI for a sec or two to wait for page loading. How can i do this in the background?
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Here i will get my attributes off the webpage ect....
End Sub

"For Each" loop : Application Freeze in Vb.net

I am using the following code to get the size of files inside a directory
and put it in Label1:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
"\windows",Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly,_
"*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
Label1.Text = Label1.Text + filesizelabel.Length
Next
The problem is that i have more than 50 for each loops (a system cleaning app).
When I run the loops my app freezes until the loops finish, even if I run one loop.
Is there a solution to make it show the name of the current file? I tried this as well, but it also froze my application:
label2.text = foundfile
The application does not respond to any click, until it finishes the loops. It shows the size in Label1 and the last scanned file in Label2. This also freezes the application:
system.threading.thread.sleep(100)
Is there any alternative to foreach or a solution to fix this issue?
Here's a quick example using Async/Await with a Button Click() Handler:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Await Task.Run(Sub()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
End Sub)
Button1.Enabled = True
End Sub
For VB.Net 2010, try this instead:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
Me.Invoke(Sub()
Button1.Enabled = True
End Sub)
End Sub
This is a prime candidate for a background worker.
Have a read about how they work, but at a high level the task is run in another thread with some events that you access in your main UI thread.
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If bw.WorkerSupportsCancellation = True Then
' this will allow the user to cancel the work part way through
bw.CancelAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' your slow code goes here
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
Derek has put a bad code, it's not working and Idle mind code does not work on .NET 2.0
Dereks approach is working if code is complete, as below:
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub app_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
..
End sub
Private Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'your work to not freeze form
end sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub

Delete file after "x" second after creation

I want to delete file after 15 second of create that file.
I use this code but no success.
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click
Dim test = Application.StartupPath & "\" + tte4
Timer1.Enabled = True
If Timer1.Interval = 0 Then
My.Computer.FileSystem.DeleteFile(test)
End If
End Sub
You must handle the timed event in a handler for the Timer's Tick Event (or Elapsed if using System.Timers.Timer):
Private m_strTest As String = String.Empty
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click
m_strTest = Application.StartupPath & "\" + tte4
Timer1.Enabled = True
End Sub
If using System.Forms.Timer (most likely):
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
System.IO.File.Delete(m_strTest)
Timer1.Enabled = False
End Sub
If using System.Timers.Timer:
Private Sub Timer1_Elapsed(ByVal source As Object, ByVal e As ElapsedEventArgs) Handles Timer1_Elapsed
System.IO.File.Delete(m_strTest)
Timer1.Stop()
End Sub
If you added your Timer from the Toolbox, the above code will work (due to WithEvents). If you added it programatically, you'll also have to wire up the Event Handler and remove the Handles from your Timer1_Tick Method:
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click
m_strTest = Application.StartupPath & "\" + tte4
AddHandler Timer1.Tick, AddressOf Timer1_Tick
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs)
System.IO.File.Delete(m_strTest)
Timer1.Enabled = False
End Sub
You need define a method that the timer fires when the interval elapses.
' Timer set to fire every 15 seconds
Dim Timer1 As New Timer(15000)
' OnTimedEvent is the method that will fire after 15 seconds
AddHandler Timer1.Elapsed, AddressOf OnTimedEvent
' Start the Timer
Timer1.Start()
The OnTimedEvent is as followed:
Private Sub OnTimedEvent(source As Object, e As ElapsedEventArgs)
' Stop the timer from firing again
Timer1.Stop()
' Delete your file
End Sub
Reference
https://msdn.microsoft.com/en-us/library/k0wdddfy(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
You have the right idea in using a Timer to wait for the deletion, but your way of trying to use it is incorrect. A (System.Windows.Forms) Timer raises an event when it ticks, and you need to tie that event to the code you want to run.
Also, to combine parts of a path it is better to use IO.Path.Combine, and when doing file operations it is a good idea to "wrap" the relevant code in a Try..Catch construct and handle the exception if it happens.
So:
Option Infer On
Option Strict On
Imports System.IO
Public Class Form1
Dim tim1 As New System.Windows.Forms.Timer
Dim tte4 As String = "afile.txt"
Private Sub Timer1_Tick(sender As Object, e As EventArgs)
tim1.Enabled = False
Dim target = Path.Combine("C:\temp", tte4)
If File.Exists(target) Then
Try
File.Delete(target)
Catch ex As Exception
MessageBox.Show(String.Format("The file ""{0}"" could not be deleted because {1}", target, ex.Message))
End Try
Else
' the file does not exist - do something if required
End If
End Sub
Private Sub SetUpTimer()
tim1.Enabled = False
tim1.Interval = 1000 * 15
' here we tie the event to the code
AddHandler tim1.Tick, AddressOf Timer1_Tick
End Sub
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles MyBase.Click
' this has the side-effect of trying to delete a file after an interval
tim1.Enabled = True
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
SetUpTimer()
End Sub
End Class
You did not show code for creating the file - I assume you used a click on the form just to demonstrate the idea.