1

I'm loading a web page into a WebBrowser object, and part of the web page has an HTML Form with 3 attributes, namely two Select attributes and one Input attribute of type Submit. The two Select attributes present different lists of choices, and having made a choice one presses the button that corresponds to the Input attribute which causes data to be loaded. When the program is running and the WebBrowser object is visible, I can manually use the mouse and the web page loads the data correctly, exactly as though it was running in a browser like e.g. Chrome. However, when I try to write C# to control the WebBrowser object, it doesn't work.

When the web page is first loaded, the Input attribute on the form is greyed out. However, using the mouse to make a choice in either of the two Select attributes causes the Input attribute to be enabled and it changes colour to green. The C# code I have to make a selection doesn't replicate that behaviour, because the Input attribute doesn't get enabled as a result of the code (below) which does the selection. It's not clear to me where the code which causes the Input attribute to be enabled is located, but I've been hoping that I don't need to work that out.

The HTML code for the form is as follows:

<form method="get" id="filter-nav">  
    <fieldset> 
        <label for="group-drop-down">Show me:</label> 
        <select id="drop-down-1" name="filter"> 
            <option value="">drop-down-1-heading</option>
            <optgroup label="optgroup-1-label" id="optgroup-1" >  
                <option value="optgroup-1-choice-1"  > Choice-1 </option> 
                <option value="optgroup-1-choice-2"  > Choice-2 </option>   
            </optgroup> 
            <optgroup label="optgroup-2-label" id="optgroup-2" > 
                <option value="optgroup-2-choice-3"  > Choice-3 </option> 
                <option value="optgroup-2-choice-4"  > Choice-4 </option>   
            </optgroup> 
        </select>
        <span>OR</span>  
        <select id=""drop-down-2" name="filter"> 
            <option value="">drop-down-2-heading</option> 
            <optgroup label="optgroup-3-label" id="optgroup-3" >  
                <option value="optgroup-3-choice-5"  > Choice-5 </option> 
                <option value="optgroup-3-choice-6"  > Choice-6 </option>   
            </optgroup> 
            <optgroup label="optgroup-4-label" id="optgroup-4" > 
                <option value="optgroup-4-choice-7"  > Choice-7 </option> 
                <option value="optgroup-4-choice-8"  > Choice-8 </option>   
            </optgroup> 
        </select>
        <input id="filter-nav-submit" type="submit" value="Update" />
    </fieldset>
</form>

and the C# code that I've been using to try and control it is the LoadData() method of the following class

private class WebBrowserHelper {
    WebBrowser wb;

    public void LoadData() {
        HtmlElement select1 = getElement("select", "drop-down-1");
        select1.Focus();
        Application.DoEvents();
        select1.SetAttribute("Value", "optgroup-1-choice-2");
        Application.DoEvents();
        select1.InvokeMember("change");
        Application.DoEvents();

        // at this point, the select1 dropdown shows the right value
        // but the button corresponding to the input attribute is still greyed out

    }
    public HtmlElement getElement(string tagName, string IdName) {
        HtmlElementCollection theElementCollection = wb.Document.GetElementsByTagName(tagName);
        foreach (HtmlElement curElement in theElementCollection) {
            object id = curElement.GetAttribute("id");
            if (id != null && id.Equals(IdName)) return curElement;
        }
        return null;
    }
}

What am I doing wrong?

Stochastically
  • 7,616
  • 5
  • 30
  • 58
  • Opinion: I would move this work to the web page and just send it in the URL to load, for example: http://mypage.html?OptGroup=2 etc.. We have something similar running in production and it's been working pretty good. – Eric Scherrer Jun 25 '14 at 18:34
  • @EricScherrer thanks for reading my query. However, if I understand you correctly, you're suggesting that I alter the web page that I'm loading into the `WebBrowser` object. Unfortunately I can't do that because it's not a web page that I control. – Stochastically Jun 25 '14 at 19:04
  • Bummer. I am not too knowledgeable on what your trying to do but my only other suggestion would be giving the System.Windows.Automation library a try and screen scrape it. – Eric Scherrer Jun 25 '14 at 19:10

1 Answers1

1

The problem might with Application.DoEvents (which BTW is an evil even for UI automation). A single iteration of the message loop pump that it does may just be not enough. Ideally, you should be using async/await and some asynchronous delay:

public async Task LoadDataAsync() {
    HtmlElement select1 = getElement("select", "drop-down-1");
    select1.Focus();
    await Task.Delay(200);
    select1.SetAttribute("Value", "optgroup-1-choice-2");
    await Task.Delay(200);
    select1.InvokeMember("change");
    await Task.Delay(200);

    // ...
}

However, if you take this road, the code has to be async "all the way down".

If that doesn't help anyway, just try enabling the button manually:

HtmlElement button = getElement("input", "filter-nav-submit");
button.Enabled = true;
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • Thanks for your suggestions, although neither of them worked. Somehow, manually using the web page must invoke a change method on the `select` attribute which for some reason isn't getting invoked by my code. And it must be doing more than enabling the button, because manually enabling and then calling `button.InvokeMember("Click")` doesn't cause the data to be loaded. Thanks anyway. – Stochastically Jun 26 '14 at 08:08
  • @Stochastically, no problem. The page probably has some dynamic JavaScript logic, so it'd be hard to help any further without being able to repro. – noseratio Jun 26 '14 at 08:14
  • (FYI, the web page that I'm looking at is http://www.bbc.co.uk/sport/football/results, and the button name on the real page and my code here is the same.) – Stochastically Jun 26 '14 at 08:15
  • @Stochastically, this page is a not an easy one to automate via DOM, but you could simulate a mouse click, like this: http://stackoverflow.com/a/22984360/1768303 – noseratio Jun 26 '14 at 08:47