Link to home
Start Free TrialLog in
Avatar of deanlee17
deanlee17

asked on

wpf htmldocument question

Hi guys,

Im trying to automatically log the user into this website, which I have in a WPF project in a browser window:

http://www.brokerforum.com/

I have the following code so far to populate the username and password

 HtmlDocument doc = (HtmlDocument)Enquiry_Sourcing_Netcomponents.Document;

                doc.GetElementById("Session_Username").SetAttribute("value", "USERNAME");
                doc.GetElementById("Session_Password").SetAttribute("value", "PASSWORD");
 

Open in new window


I now need to call the click button to login. The code (I believe) is...

doc.GetElementById("").InvokeMember("click");

Open in new window


However the click button has no id. So how do I call this?

The html / css behind the button is:

<div class="formActions">
                                <input src="/tbf/img/login-button-small-en.png" type="image" />
                            </div>

Open in new window


Thanks,
Dean
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

You might be able to get a reference to the <input> element by using the HtmlDocument.GetElementsByTagName method, and checking the "src" attribute for the desired image name.
That's not a button, though--it's an image. You might need to invoke some Javascript on that one.
Avatar of deanlee17
deanlee17

ASKER

TheLearnedOne: Any idea how to code this?
HtmlDocument doc = (HtmlDocument)Enquiry_Sourcing_Netcomponents.Document;

            doc.GetElementById("Session_Username").SetAttribute("value", "XXXXX");
            doc.GetElementById("Session_Password").SetAttribute("value", "XXXXX");
            doc.GetElementById("").InvokeMember("click");

            HtmlElementCollection theElementCollection = doc.GetElementsByTagName("input");

            HtmlElementCollection elems = doc.GetElementsByTagName("input");

            foreach (HtmlElement elem in theElementCollection)
            {
                String nameStr = elem.GetAttribute("name");
                if (nameStr != null && nameStr.Length != 0)
                {
                    String contentStr = elem.GetAttribute("content");
                    System.Windows.MessageBox.Show("Document: " + Enquiry_Sourcing_Netcomponents.ToString() + "\nDescription: " + contentStr);
                }
            }

Open in new window


Throwing error on first line:

System.InvalidCastException was unhandled
  HResult=-2147467262
  Message=Unable to cast COM object of type 'mshtml.HTMLDocumentClass' to class type 'System.Windows.Forms.HtmlDocument'. Instances of types that represent COM components cannot be cast to types that do not represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface.
Aah, yes, the ambiguous nature of HtmlDocument.  If that reference is an HTMLDocument, then you need to change some things around, and use different methods, which gets trickier, and a lot heavier.

Word of warning:  "If you are going to add a reference to mshtml, don't use a using mshtml; statement at the top of the module, since it will slow Intellisense to a crawl"
Ah ok, all ive added is 'using System.Windows.Forms;'

Now... Im stuck lol
Microsoft mshtml is COM library that is part of the .NET framework, but there is a managed wrapper available as a reference.

Example using mshtml:

  var htmlDoc = (HTMLDocument)webBrowser1.Document;

   //find the search text box..
   var searchTextBox = (HTMLInputElement)htmlDoc.all.item("p", 0);
   searchTextBox.value = "Search text goes here";

   //find the button
   var searchButton = (HTMLInputElement)htmlDoc.all.item("searchsubmit", 0);
   searchButton.click();

Open in new window

Ok that makes sense. So do I add a reference to 'mshtml.HTMLDocumentClass' in the right hand window but then don't include a 'using' statement at the top?

In your example, is 'searchsubmit' the actual id as the button I need to hit doesn't have the id. Im assuming we could do that search on the input source (button image)?
Here are some screen shots from Visual Studio 2012, on how to add a reference to the Microsoft.mshtml assembly.

User generated image
 User generated image
Ok cool, and im doing ok with the below code, it populates the input boxes, now I just need to find a way to click a button without an id or class attached...

            var htmlDoc = (HTMLDocument)LoginWebpage.Document;

            //find the search text box..

            var UsernameTextBox = (mshtml.HTMLInputElement)htmlDoc.all.item("Session_Username", 0);
            UsernameTextBox.value = "XXXXX";

            var PasswordTextBox = (mshtml.HTMLInputElement)htmlDoc.all.item("Session_Password", 0);
            PasswordTextBox.value = "XXXXX";

Open in new window


Thanks,
Dean
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Where did HtmlHelper come from?

I get 'The name 'HtmlHelper' does not exist in the current context'

Does this require another include?
HtmlHelper is the class that I created and posted, starting with this line:

internal static class HtmlHelper
d'oh, I missed that.

Ok so implemented it and its throwing an error on

'var htmlDocument = (mshtml.HTMLDocument)document.DomDocument;'

saying....Object reference not set to an instance of an object?

Thanks,
Dean
Code so far:
  internal static class HtmlHelper
        {

            public static object GetElementByTagNameWithAttribute(
                mshtml.HTMLDocument document, string tagName, string attributeName, string attributeValue)
            {
                var elementList = document.getElementsByTagName(tagName);

                foreach (dynamic element in elementList)
                {
                    var attributeList = (mshtml.IHTMLAttributeCollection)element.attributes;

                    foreach (var attribute in attributeList)
                    {
                        var htmlAttribute = (mshtml.IHTMLDOMAttribute)attribute;
                        if (htmlAttribute.nodeName == attributeName && htmlAttribute.nodeValue == attributeValue)
                            return element;
                    }
                }

                return null;
            }

        } 

        private void btnCreateQuote_Click(object sender, RoutedEventArgs e)
        {

            var htmlDoc = (HTMLDocument)LoginWebpage.Document;


            var UsernameTextBox = (mshtml.HTMLInputElement)htmlDoc.all.item("Session_Username", 0);
            UsernameTextBox.value = "XXX";

            var PasswordTextBox = (mshtml.HTMLInputElement)htmlDoc.all.item("Session_Password", 0);
            PasswordTextBox.value = "XXX";


            var browser = new System.Windows.Forms.WebBrowser();
            var document = browser.Document;
            var htmlDocument = (mshtml.HTMLDocument)document.DomDocument;

            var buttonElement = (mshtml.HTMLInputElement)HtmlHelper.GetElementByTagNameWithAttribute(htmlDocument,
                        "input", "src", "/tbf/img/login-button-small-en.png");

            buttonElement.click(); 

}

Open in new window

Is that code working?
'var htmlDocument = (mshtml.HTMLDocument)document.DomDocument;'

saying....Object reference not set to an instance of an object?
If you got that code from me, that was meant to show you an example, using the WebBrowser control.  

I thought this is your HTMLDocument reference:

var htmlDoc = (HTMLDocument)LoginWebpage.Document;
Hmmm this is still erroring. Will provide furthur info shortly..
Hi The LearnedOne,

Sorry for the delay, I have been away from work for a while.

As per your code and comments above, for the line below:

 var htmlDocument = (mshtml.HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.DomDocument;

I get the error:

'System.Windows.Controls.WebBrowser' does not contain a definition for 'DomDocument' and no extension method 'DomDocument'

Complete code as it stands....

 internal static class HtmlHelper
        {

            public static object GetElementByTagNameWithAttribute(
                mshtml.HTMLDocument document, string tagName, string attributeName, string attributeValue)
            {
                var elementList = document.getElementsByTagName(tagName);

                foreach (dynamic element in elementList)
                {
                    var attributeList = (mshtml.IHTMLAttributeCollection)element.attributes;

                    foreach (var attribute in attributeList)
                    {
                        var htmlAttribute = (mshtml.IHTMLDOMAttribute)attribute;
                        if (htmlAttribute.nodeName == attributeName && htmlAttribute.nodeValue == attributeValue)
                            return element;
                    }
                }

                return null;
            }

        } 


        private void EnquirySave_Click(object sender, RoutedEventArgs e)
        {
 
            //ascentEntities.SaveChanges();
            var htmlDoc = (HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.Document;


            var UsernameTextBox = (mshtml.HTMLInputElement)htmlDoc.all.item("Session_Username", 0);
            UsernameTextBox.value = "XXX";

            var PasswordTextBox = (mshtml.HTMLInputElement)htmlDoc.all.item("Session_Password", 0);
            PasswordTextBox.value = "XXX";


            var browser = new System.Windows.Forms.WebBrowser();
            var document = browser.Document;
            var htmlDocument = (mshtml.HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.DomDocument;


            var buttonElement = (mshtml.HTMLInputElement)HtmlHelper.GetElementByTagNameWithAttribute(htmlDocument,
                        "input", "src", "/tbf/img/login-button-small-en.png");

            buttonElement.click(); 



        }

Thanks

Open in new window

This line should be changed:

 var htmlDocument = (mshtml.HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.DomDocument;


to

 var htmlDocument = (mshtml.HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.Document.DomDocument;
Attached
error.png
What is the type for "WebBrowser_Sourcing_BrokerForum_Enquiry"?
It's WebBrowser
Can you show me the declaration, and tell me what version of the .NET IDE that you are using?
.net 4.5

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using DevExpress.Xpf.Core;
using DevExpress.Xpf.Ribbon;
using DevExpress.Xpf.Bars;
using DevExpress.Xpf.Layout.Core;
using DevExpress.Xpf.Docking;
using System.Data.OleDb;
using System.Data;
using DevExpress.Xpf.Grid;
using System.Data.SqlClient;
using System.IO;
using System.Globalization;
using DevExpress.XtraPrinting;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.Data.Filtering;
using System.Collections;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.Xpf.Map;
using System.Windows.Forms;
using DevExpress.Xpf.Editors;
Not the using statements, the declaration in the .Designer.cs file.
In model1.designed.cs?

I don't know what you want from in there.
I am confused, because I have 2012 IDE, a Windows Forms 4.5 application, and the System.Windows.Forms.WebBrowser.Document has the DomDocument property, as shown in the attached snapshot.

User generated image
It's a WPF application, would that make a difference?
That is what I was trying to get straight.  So, you are using the System.Windows.Controls.WebBrowser, and not the System.Windows.Forms.WebBrowser.

Try this instead:

 var htmlDocument = (mshtml.HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.Document;

Open in new window

Got there in the end:

 try
            {


                var htmlDoc1 = (HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.Document;


                var UsernameTextBox = (mshtml.HTMLInputElement)htmlDoc1.all.item("Session_Username", 0);
                UsernameTextBox.value = "XXXXX";

                var PasswordTextBox = (mshtml.HTMLInputElement)htmlDoc1.all.item("Session_Password", 0);
                PasswordTextBox.value = "XXXXX";



                foreach (mshtml.HTMLInputElement HTMLI in ((mshtml.HTMLDocument)WebBrowser_Sourcing_BrokerForum_Enquiry.Document).getElementsByTagName("input"))
                {

                    if (HTMLI.getAttribute("type").Equals("image"))
                    {
                        if (HTMLI.src == "http://www.brokerforum.com/tbf/img/login-button-en.png")
                        {

                            HTMLI.click();
                            return;
                        }
                    }


                }
            }

            catch (Exception) { }

Open in new window