Obtain child anchor element within WebBrowser control - c#

Preamble
I'm using the WebBrowser control, which a user will interact with, so a solution will need to work with a visible WebBrowser control.
Question
How do I check if an element has an anchor as a child? All browsers are able to distinguish that an element contains an anchor (<a href=""...), and offers "open in new tab" functionality. That is what I am attempting to replicate. However, when I right click on a HtmlElement I'm only able to obtain the parent element.
Example
Taking the BBC website as an example, when I right click on the highlighted element (picture below), my output is DIV, but viewing the source code there is an anchor element as a child of this div.
SSCCE
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace BrowserLinkClick
{
public partial class Form1 : Form
{
private WebBrowser wb;
private bool firstLoad = true;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
wb = new WebBrowser();
wb.Dock = DockStyle.Fill;
Controls.Add(wb);
wb.Navigate("http://bbc.co.uk");
wb.DocumentCompleted += wb_DocumentCompleted;
}
private void Document_MouseDown(object sender, HtmlElementEventArgs e)
{
if (e.MouseButtonsPressed == MouseButtons.Right)
{
HtmlElement element = wb.Document.GetElementFromPoint(PointToClient(MousePosition));
//I assume I need to check if this element has child elements that contain a TagName "A"
if (element.TagName == "A")
Debug.WriteLine("Get link location, open in new tab.");
else
Debug.WriteLine(element.TagName);
}
}
private void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (firstLoad)
{
wb.Document.MouseDown += new HtmlElementEventHandler(Document_MouseDown);
firstLoad = false;
}
}
}
}
Please test any proposed solution using the BBC website and the highlighted headline (the headline changes, but the DOM remains the same).

You have to get the child elements of element before checking if it's an anchor:
HtmlElement element = wb.Document.GetElementFromPoint(PointToClient(MousePosition));
foreach (HtmlElement child in element.Children)
{
if (child.TagName == "A")
Debug.WriteLine("Get link location, open in new tab.");
}

To access the needed properties you need to cast the HtmlElement to one of the unmanaged MSHTML interfaces, e.g. IHTMLAnchorElement
You have to add Microsoft HTML Object Library COM reference to your project.
(The file name is mshtml.tlb.)
foreach (HtmlElement child in element.Children)
{
if (String.Equals(child.TagName, "a", StringComparison.OrdinalIgnoreCase))
{
var anchorElement = (mshtml.IHTMLAnchorElement)child.DomElement;
Console.WriteLine("href: [{0}]", anchorElement.href);
}
}
There are plenty of such interfaces but MSDN will help you choose. :)
Scripting Object Interfaces (MSHTML)

I propose you the following solution:
url variable will have url of your desired output, you'll be able to see it in debugger window.
private void Document_MouseDown(object sender, HtmlElementEventArgs e)
{
if (e.MouseButtonsPressed == MouseButtons.Right)
{
HtmlElement element = wb.Document.GetElementFromPoint(PointToClient(MousePosition));
//I assume I need to check if this element has child elements that contain a TagName "A"
if (element.TagName == "A")
{
Debug.WriteLine("Get link location, open in new tab.");
var urlRaw = element.OuterHtml;
string hrefBegin = "href=";
var idxHref = urlRaw.IndexOf(hrefBegin) + hrefBegin.Length + 1;
var idxEnd = urlRaw.IndexOf("\"", idxHref + 1);
var url = urlRaw.Substring(idxHref, idxEnd - idxHref);
Debug.WriteLine(url);
}
else
Debug.WriteLine(element.TagName);
}
}

There has to be something else wrong with your program. On the BBC website your code works for the news articles (although I see the non UK version of the site). On other websites where there are anchor elements as children the code below works
private void Document_MouseDown(object sender, HtmlElementEventArgs e)
{
if (e.MouseButtonsPressed == MouseButtons.Right)
{
HtmlElement element = wb.Document.GetElementFromPoint(PointToClient(MousePosition));
if (element.Children.Count > 0)
{
foreach (HtmlElement child in element.Children)
{
if (child.TagName == "A")
Debug.WriteLine("Get link location, open in new tab.");
}
}
else
{
//I assume I need to check if this element has child elements that contain a TagName "A"
if (element.TagName == "A")
Debug.WriteLine("Get link location, open in new tab.");
else
Debug.WriteLine(element.TagName);
}
}
}

The challenge with bbc web site, that it have little bit non standard approach toward their url.
Below goes one of the samples of their a href:
<A tabIndex=-1 aria-hidden=true class=block-link__overlay-link href="http://www.bbc.com/news/world-africa-36132482" rev=hero5|overlay>Barbie challenges the 'white saviour complex' </A>
so, you need to use two parts in if:
1. element.TagName == "A"
2. check attribute href like this: element.GetAttribute("href")
Those two checks can give you guaranty that you deal with something with tag a, and that tag a has attribute href. See another example of usage:
private void Document_MouseDown(object sender, HtmlElementEventArgs e)
{
if (e.MouseButtonsPressed == MouseButtons.Right)
{
HtmlElement element = wb.Document.GetElementFromPoint(PointToClient(MousePosition));
//I assume I need to check if this element has child elements that contain a TagName "A"
if (element.TagName == "A" && !string.IsNullOrEmpty(element.GetAttribute("href")))//it means we have deal with href
{
Debug.WriteLine("Get link location, open in new tab.");
var url = element.GetAttribute("href");
Debug.WriteLine(url);
}
else
Debug.WriteLine(element.TagName);
}
}

Related

How do i input data and press buttons in a webbrowser using c#?

when you press the submit button i want the data in the textboxes to be filled into the webbrowser (inside the windows form) and for the login button to be pressed
I could not find the answer anywhere and i want to use this to make something automated
i currently have no code except
webBrowser1.Navigate("https://en-gb.facebook.com/");
currently it doesnt function but i want to find out how to do this in a simple way as i am not that good at c# yet.image of what it looks like, this will make my question make more sense
The following code will help you
Form Load event
private void Form_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("https://en-gb.facebook.com/");
}
Button Click event
private void button1_Click(object sender, EventArgs e)
{
// User name
HtmlElement userElement = (from HtmlElement element in webBrowser1.Document.GetElementsByTagName("input") select element)
.Where(x=>x.Id == "email").FirstOrDefault();
if (userElement != null)
{
userElement.SetAttribute("value", textBox1.Text);
}
// Password
HtmlElement passwordElement = (from HtmlElement element in webBrowser1.Document.GetElementsByTagName("input") select element)
.Where(x => x.Id == "pass").FirstOrDefault();
if (passwordElement != null)
{
passwordElement.SetAttribute("value", textBox2.Text);
}
// Submit
HtmlElement submitElement = (from HtmlElement element in webBrowser1.Document.GetElementsByTagName("input") select element)
.Where(x => x.Id == "u_0_a").FirstOrDefault();
if (submitElement != null)
{
submitElement.InvokeMember("click");
}
}

DocumentCompleted firing only once

I have a WebBrowser inside a form and I want to do some automation with it. I click a button inside a windows form that commands the Navigate method of the WebBrowser to a certain page. Then I automatically click a link after DocumentCompleted has fired but after that I want to also click a Button that exists in the new page that appeared by clicking the link. It seems DocumentCompleted fires only when I click the button in the windows form not when I automatically click the a link inside the webpage.
void BtnTestClick(object sender, EventArgs e)
{
webBrowser1.Navigate(#"https://play.google.com/apps/");
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
}
public void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webBrowser = sender as WebBrowser;
//webBrowser.DocumentCompleted -= WebBrowser_DocumentCompleted;
// test to see if we're on fist CONFIRM page then go forward by clicking
var links = webBrowser1.Document.GetElementsByTagName("a");
foreach (HtmlElement link in links)
{
if (link.InnerText == "Proceed anyway")
{
link.InvokeMember("click");
}
} // this works
webBrowser1.Document.GetElementById("gwt-uid-126").InvokeMember("click");
}
After the link.InvokeMember("click"); a new page loads in the webbrowser that has a button which I also want to click ( gwt-uid-126 )
But it doesn't get clicked.
I've also tried:
var elements = webBrowser1.Document.GetElementsByTagName("button");
foreach (HtmlElement file in elements)
{
if (file.GetAttribute("class") == "GKYRWGTDNX GKYRWGTDLY")
{
file.Focus();
file.InvokeMember("click");
}
}
With no luck!
From what I see, second click doesn't work because the document is not completely loaded and second click is invoked.
You will have to add another if-else block that handled second document load.
Edit1: I was on phone when I answered this, so couldn't provide any snippet. Following is the change that you can do WebBrowser_DocumentCompleted method.
var links = webBrowser1.Document.GetElementsByTagName("a");
foreach (HtmlElement link in links)
{
if (link.InnerText == "Proceed anyway")
{
link.InvokeMember("click");
}
}
// following is for the page that is loaded on click of link.
var gwt_uid_126 = webBrowser1.Document.GetElementById("gwt-uid-126");
if(gwt_uid_126 != null)
{
gwt_uid_126.InvokeMember("click");
}
You might want to check if the WebBrowser_DocumentCompleted method is actually being called on second page load. This might be the reason why second click is not registering.
move this part of code in the Constructor or Form_Load:
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
try this instead of using WebBrowserDocumnetCompletedEventHandler:
void btnTestClick(object sender, EventArgs e)
{
webBrowser1.Navigate(#"https://www.google.com/");
while (webBrowser1.ReadyState != WebBrowserReadyState.Complete)
continue;
var webBrowser = sender as WebBrowser;
//webBrowser.DocumentCompleted -= WebBrowser_DocumentCompleted;
// test to see if we're on fist CONFIRM page then go forward by clicking
var links = webBrowser1.Document.GetElementsByTagName("a");
foreach (HtmlElement link in links)
{
if (link.InnerText == "Proceed anyway")
{
link.InvokeMember("click");
}
} // this works
webBrowser1.Document.GetElementById("gwt-uid-126").InvokeMember("click");
}

Not able to set Url property at runtime in webbrowser control

I have a webbrowser control in my Winform application.
Below regions belong to division of code sample.
Region 1
The current url page loaded is "http://MyWebsite.com". I am clicking a link (say "About Us") in the web page using code. This click will take me to new url page ("http://MyWebsite.com/About_Us"). In Navigating event I am recording this new url.
Region 2
Now I want to get all elements of this new url and click on a new link. But not sure how to do it. In Region 2 I am also assigning the new url to webbrowser object. But nothing reflects in the instance. webbrowser.url still contains the previous url path.
I have following code for button click:
private void Button1Click(object sender, EventArgs e)
{
// Region 1---------------------------------------------
HtmlElementCollection links = webBrowser1.Document.GetElementsByTagName("A");
foreach (HtmlElement link in links)
{
if (link.InnerText != null && link.InnerText.Equals("Click to view magic"))
{
link.InvokeMember("Click");
break;
}
}
// EndRegion---------------------------------------------
// Region 2---------------------------------------------
webBrowser1.Url = new Uri(_url.AbsoluteUri, UriKind.Absolute);
webBrowser1.Navigate(_url); //New Edit
links = webBrowser1.Document.GetElementsByTagName("input");
foreach (HtmlElement link in links)
{
if ((link.GetAttribute("Name") == "BooHoo"))
{
link.InvokeMember("Click");
break;
}
}
// EndRegion---------------------------------------------
}
private void WebBrowser1Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
_url = e.Url;
}
Can anyone help me to do this. The question may not be very clear. Please let me know if you need any further details. Thanks.
So it was tricky a bit or else I am careless. I was watching the property values in debug mode. Later I noticed that, after pressing F5 in Visual Studio (continue debugging) and all the methods are run, webbrowser shows the changed values.
Hope it helps.
You need to subscribe Navigated event as WebBrowser works asynchronously.
private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
--Do Something Here....
}

How to capture click event for any button inside in web browser control?

Suppose my web browser is showing a html page where many buttons are there. I just like to know how could I capture click on any button inside web browser control from my c# win apps.
If it is possible then from that event i want to capture button name,height and width and any custom property. etc. Please guide me.
This will be helpful if you want to capture only mouse clicks:
WebBrowser _browser;
this._browser.DocumentCompleted+=new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted);
...
private void browser_DocumentCompleted(Object sender, WebBrowserDocumentCompletedEventArgs e)
{
this._browser.Document.Body.MouseDown += new HtmlElementEventHandler(Body_MouseDown);
}
...
void Body_MouseDown(Object sender, HtmlElementEventArgs e)
{
switch(e.MouseButtonsPressed)
{
case MouseButtons.Left:
HtmlElement element = this._browser.Document.GetElementFromPoint(e.ClientMousePosition);
if(element != null && "submit".Equals(element.GetAttribute("type"),StringComparison.OrdinalIgnoreCase)
{
}
break;
}
}
can u please tell me how can i read custom attribute of any html
element loaded inside web browser control. thanks
If You don't want to link to "Microsoft.mshtml", You can try to use this sample method. But you can't read all members thru reflection:
public static String GetElementPropertyValue(HtmlElement element, String property)
{
if(element == null)
throw new ArgumentNullException("element");
if(String.IsNullOrEmpty(property))
throw new ArgumentNullException("property");
String result = element.GetAttribute(property);
if(String.IsNullOrEmpty(result))
{//В MSIE 9 получить свойство через DomElement не получается. Т.к. там он ComObject.
var objProperty = element.DomElement.GetType().GetProperty(property);
if(objProperty != null)
{
Object value = objProperty.GetValue(element.DomElement, null);
result = value == null ? String.Empty : value.ToString();
}
}
return result;
}

webbrowser control SetAttribute does not respond programmatically

i have application and i need to add text programmatically to some fields it works in most pages but in www.google.com when i try to but value to search, it did not work until i clicked on the text area then the value appear
is there any way to get around this
my code:
HtmlElementCollection el = webBrowser1.Document.All;
foreach (HtmlElement H in el)
{
if (H.GetAttribute("type").Equals("text") )
H.SetAttribute("value", sendtext);
}
i tried to click on it programmatically
object obj = H.DomElement;
System.Reflection.MethodInfo mi = obj.GetType().GetMethod("click");
mi.Invoke(obj, new object[0]);
also it does not work
Project + Add Reference, Browse tab and select c:\windows\system32\mshtml.tlb (.dll on earlier Windows versions). This gives you access to the native COM interface that the DomElement property returns. So you can write your code cleanly like this:
var obj = (mshtml.IHtmlElement)H.DomElement;
obj.click();
Or you can do it a bit less cleanly with the HtmlElement.InvokeMember() method:
H.InvokeMember("click");
A sample form that runs a google query using this technique:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
webBrowser1.Url = new Uri("http://google.com");
webBrowser1.DocumentCompleted += webBrowser1_DocumentCompleted;
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
if (webBrowser1.Url.Host.EndsWith("google.com")) {
HtmlDocument doc = webBrowser1.Document;
HtmlElement ask = doc.All["q"];
HtmlElement lucky = doc.All["btnI"];
ask.InnerText = "stackoverflow";
lucky.InvokeMember("click");
}
}
}

Categories

Resources