vb.net webbrowser documentcompleted event - page not loading - vb.net

I have searched the other questions surrounding the WebBrowser DocumentCompleted event, but no-one seems to have the exact problem that I am having.
I am trying to automate searching flights on an airline website. The first url I use is the same every time except for the date part so it's easy enough to get the WebBrowser to go the URL by combining strings. However, that page is a disclaimer page that has a 'proceed' button that needs to be clicked before prices are shown. If I use a series of buttons on a form, I can get to the first URL by clicking button1, and then click the proceed button by clicking button2. It works fine.
However, I wanted to remove the need to click button2 so attempted to use the WebBrowser DocumentCompleted event. The problem that I am having is that the first page never seems to fully load in the webbrowser and so the button is never clicked.
This is the code that I am using for the two buttons and the DocumentCompleted event
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.Text = fullURL
WebBrowser1.Navigate(fullURL)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim allElements As HtmlElementCollection = WebBrowser1.Document.All
For Each webpageelement As HtmlElement In allElements
If webpageelement.GetAttribute("src") = proceedbuttonattribute Then
webpageelement.InvokeMember("click")
End If
Next
End Sub
Private Sub WebBrowser1_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
If TextBox1.Text.StartsWith(firstURL) = True Then 'make sure that button is only clicked after first webpage loads
Dim allElements As HtmlElementCollection = WebBrowser1.Document.All
'Click 'Proceed to Booking' button
For Each webpageelement As HtmlElement In allElements
If webpageelement.GetAttribute("src") = proceedbuttonattribute Then
webpageelement.InvokeMember("click")
End If
Next
End If
End Sub
Thanks!

WebBrowser1.Navigate(fullURL)
Do While wb.ReadyState <> WebBrowserReadyState.Complete
Application.DoEvents()
Loop
;)

When you say DocumentCompleted is never triggered, do you mean it isn't triggered at all or are you saying that maybe the If statement isn't returning True, and therefore the contents (and click action) are not run?
Also, are there any frames or iframes on this page? Because, if there are, DocumentCompleted will not run until each and every single frame is loaded and completed, and if its an ajaxed frame, then that will introduce mroe problems. All it takes is for one of the frames to not load properly or remain in "Interactive" mode (where .readystate = 3) instead of it being fully and properly loaded (where .readystate = 4) and this will prevent the DocumentCompleted event from getting triggered.
Also, how long are you waiting for the DC event to trigger?
There is a better way around this, all you need to do is run a Do/While loop, with the exact same code as in your DC event, and it will just sit there (after the .Navigate2 is called) and just wait until that button shows up in the DOM, and as soon as the following returns True, you can use .InvokeMember and click on the button.
If webpageelement.GetAttribute("src") = proceedbuttonattribute Then
So, in this case, you will create another function named "WaitUntilButtonFound" and perhaps place a 100 millisecond Sleep (wait) between each loop, and a .DoEvents (found in .Threading namespace) right after or before the Sleep method (also found in .Threading I think).
This way, as soon as the button of relevance appears in the Document Object Model, you can click it, and if you want, as soon as its found, you can wait another 2 - 3 seconds (if you want, no real need) and then click it. Because finding that button in the DOM is an indicator that the page has either loaded, or partially loaded (where the relevant or necessary part has completed loading), so that you can resume the action you wanted to take on that button (which is, to click it), right after it appears. In fact, it'll be the quickest way to move forward as well.
What do you think? Let me know how you go and if you need more help or guidance. Also, if you could let us know if the DC event is absolutely not being triggered or if it's just your IF statement that is blocking the DC from running the code inside the DC event, that would be helpful, because if the DC event is being triggered but code inside isn't running because of the If statements inside it, that is something entirely different to the DC even not being triggered at all.
Let us know, thanks.

Related

clicking button1 or button2 then clicking button3 to make a panel be visible or not

It's supposed to work when I first click either btn1 or btn2, then secondly clicking the btnFinal, then the panelhide will work.
Below is the one I have been trying to do:
Private Sub btnFinal_Click(sender As Object, e As EventArgs) Handles btnFinal.Click
If Me.btn1.Focus() Then
panelhide.Visible = False
End If
If Me.btn2.Focus() Then
panelhide.Visible = True
End If
End Sub
It should be visible on the output on which button (btn1 or btn2) is highlighted/focused. If-statement is not really necessary if there are other ways for this to work.
It kept focusing on btn2 after clicking btnFinal. I tried ElseIf on btn2 but this time it kept focusing on btn1. I don't know what I'm doing wrong but I kind of figured it's on the Focus(), but I might be wrong. Maybe everything's wrong and I would like to apologize in advance. I have been doing the trial-and-error method but this one's I cannot figure out.

More info on click event in VB

I would like to create a bot that repeats the same action many times. Is there any way I can get more info about a click event. Lets say I click somewhere on the opened file explorer window. Can the program tell that I clicked on a specific area or button in that window? Can I make the program click or type on a specific(opened) window?
Thanks
Yes the program can tell when you click on a specific button. Whatever button it is that you clicked on will have its click event fired, if it exists. In order to manually tell the program to perform this action, you can implement the phrase..
btn.PerformClick()
where "btn" is the name of whatever button you are needing to click. Whenever this phrase is called, the btn_Click event handler will be fired just the same as if you yourself actually clicked the button.
To do the same thing with an actual window or form in your program is trickier because there are no built in methods you can call to trigger a windows form click event. But it is possible. The code that will define a window's event handler and trigger it will be..
Private Sub myWindow_Click() Handles MyBase.Click
'code you want to run on click event here
End Sub
myWindow_Click() 'where you want the click to be triggered
The above would be much simpler to simulate in a method call however.
And lastly to simulate the typing of text into a window, you could simply access the text property of the control you are wanting to alter, and then change that text property to the desired text. But judging from your question, you are wanting this to be done in a similar fashion to how a human would.
This can also be accomplished with some work, using a timer interval that appends to the text of the control.
Lets say for example, you wanted "hello world" to be typed into a text box on screen as a person would. Add a timer control to your form and set its interval to 250 or however fast you want the word to be type and in a method..
Dim str as String = ""
Dim pos as Integer = 0
Public Sub humanType(word as String)
str = word
Timer1.enabled = true
End Sub
public Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
myTextBox.Text = myTextBox.Text + str[pos]
pos = pos + 1
If pos = str.length Then
Timer1.enabled = false
End If
End Sub
If you have never worked with timer intervals before, this tutorial has some good info on them for VB.Net. http://www.techrepublic.com/article/perform-actions-at-set-intervals-with-vbnets-timer-control/
Hope it helps!

vb.net how to solve 2 issues with single and double click on notify icon

I have a vb.net (.NET 3.0) app which has a NotifyIcon in the system tray. I would like the single left-click and double left-click events to do different things; .Click should open the app's context menu, and .DoubleClick should take some default action. So this is my code at the moment:
Private Sub showMenu(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles Tray.Click
Debug.Print("click")
If e.Button = MouseButtons.Left Then
Dim mi As MethodInfo = GetType(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance Or BindingFlags.NonPublic)
mi.Invoke(Tray, Nothing)
End If
End Sub
Private Sub defaultAction(ByVal sender As System.Windows.Forms.NotifyIcon, ByVal e As System.EventArgs) _
Handles Tray.DoubleClick
Debug.Print("double click")
doDefaultAction()
End Sub
The first problem is that the .Click handler is fired even for a double-click - it responds to the first click rather than waiting to see if it was actually a double-click. Maybe this is normal behaviour, but is there a 'best practice' way of trapping that occurrence without horrible kludges involving timers? From what I've read on SO, I suspect not. However, that's not the most serious problem...
The second, bigger, problem is that the doDefaultAction() code does various things, one of which is to download an xml file from a specific URL. I'm doing this with this line of code (note not the actual URL:):
Dim reader = XmlReader.Create("http://server.com/genxml.php")
As soon as execution reaches that line, another .Click event is fired, so the debug output looks like this:
click
double click
click
That second click event re-opens the context menu, and because doDefaultAction() goes on to show a modal MessageBox, the menu gets stuck open. I've stepped through in the debugger, and if I 'Step Into' that Dim reader line, I get taken straight to Sub showMenu() above. Very odd. Any ideas what could cause that?

Accessing HTML buttons

There are 17 HTML buttons on a website. These Buttons are without ID and have same name,type and value. I want to click the Buttons programatically by using vb.net 2008 webbrowser control. When i write this code
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
WebBrowser1.Navigate("website bla bla")
End Sub
Private Sub WebBrowser1_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
Dim allelements As HtmlElementCollection = WebBrowser1.Document.All
For Each webpageelement As HtmlElement In allelements
If webpageelement.GetAttribute("value") = "Start" Then
webpageelement.InvokeMember("click")
End If
Next
End Sub
End Class
Then each time, the 17th button is clicked by my program. Where i want to click the 1st button individually , the 2nd button , the 3rd button and so on the next buttons individually each. So can you please guide me how to click each button individually. Here is the HTML Code of the buttons.
Your last button gets clicked because all buttons have, as you claim, the same value. Hence you should not identify the button by value.
The best solution would be to modify the server side so that the buttons have some property (ideally the id) that makes it easy to distinguish them from each other programmatically. (This would apply, for example, if you are writing a test harness for a web site that your colleagues develop. Talk to them and call your request "testability".)
However, if you absolutely have to work with the website as is, there is one bit of information that makes each button unique and that is its sequential index.
Dim i As Integer
Dim allButtons As HtmlElementCollection
allButtons = WebBrowser1.Document.getElementsByTagName('input');
i = 0
For Each webpageelement As HtmlElement In allButtons
i += 1
If i = 7 Then
webpageelement.InvokeMember("click")
End If
Next
This will click the seventh's button. The drawback of this method is that every time the layout of the website changes a little, your client code will break down severely causing all kinds of confusion and tedium before it is fixed. You can partly fight that by representing these fixed index values with symbolic names and comparing any sequential indexes only to those, but it is still a fragile technique with difficult reusability.

is there a better way to do this

so i have a loop in vb.net that loads a webpage, fills out a form and clicks submit
I currently am using these for, respectively, waiting for the webpage to load, filling out the form, and clicking submit
Do While Not browser.ReadyState = WebBrowserReadyState.Complete
System.Windows.Forms.Application.DoEvents()
Loop
and
browser.Document.GetElementById("text").SetAttribute("value", message)
and
For Each element As HtmlElement In browser.Document.GetElementsByTagName("input")
If element.GetAttribute("type") = "submit" Then
element.InvokeMember("click")
End If
Next
but my problem is that after around the fifth time the loop is run it srrors on the line with the set attribute. And i have a feeling that it is not waiting for the webpage to load before it tries filling out the form, and that is why it is erroring.
Does anyone know a better way to do this?
You can use DocumentCompleted Event, this event ensures your document is ready, and all required sections loaded
Private Sub browser_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles browser.DocumentCompleted
' YOUR FORM FILLING CODE HERE
End Sub
and for form submit you can use, forms' submit() method like this
browser.Document.GetElementById("text").DOMElement.form.submit()
Maybe the element "text" isn't loaded yet or isn't on the page?
You could make some javascript to do that task, and inject it into the page.
Here is how to make a "click" in javascript:
https://developer.mozilla.org/en/DOM/document.createEvent