1

I'm working on an application that it's been giving me some trouble, partly because of a function I made called NavigateAndWait(string url) that navigates and then waits in a loop like this:

while(!DocCompleted) Application.DoEvents();

DocCompleted is a bool that changes to true whenever DocumentCompleted is called. I keep reading everywhere that using Application.DoEvents() is in general a bad idea. And I also think it is. I was using it mainly as a temporary way of simplifying my code. The problem is that I assumed there would be another way to make a NavigateAndWait function, and I made the whole program based on this assumption. So I came up with this way of simulating a NavigateAndWait function without actually waiting using yield return. This is just a sample application that illustrates the idea (see Get() in Form1). So before I go ahead and change my whole code I would like to know your opinion. It might be actually a terrible idea because of something I'm missing (I tend to miss stuff :-)).

public partial class Form1 : Form
{
    BrowserWrapper bw;

    public Form1()
    {
        InitializeComponent();
        bw = new BrowserWrapper(webBrowser1);
    }

    public IEnumerable<string> Get()
    {
        yield return "www.google.com";      // simulates NavigateAndWait("www.google.com");
        yield return "www.yahoo.com";       // simulates NavigateAndWait("www.yahoo.com");
        yield return "www.hotmail.com";     // simulates NavigateAndWait("www.hotmail.com");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bw.CallEnumerator(Get());
    }
}

public class BrowserWrapper
{
    WebBrowser Browser;
    IEnumerator<string> Enumerator = null;

    public BrowserWrapper(WebBrowser browser)
    {
        Browser = browser;
        Browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(Browser_DocumentCompleted);
    }

    void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        if(Browser.ReadyState == WebBrowserReadyState.Complete && e.Url == Browser.Url)
        if (Enumerator != null)
        {
            if (Enumerator.MoveNext()) Browser.Navigate(Enumerator.Current);
        }
    }

    public void CallEnumerator(IEnumerable<string> enumerable)
    {
        Enumerator = enumerable.GetEnumerator();
        if (Enumerator.MoveNext())
        {
            Browser.Navigate(Enumerator.Current);
        }
    }
}
Juan
  • 15,274
  • 23
  • 105
  • 187
  • 1
    I already showed you how to run WB on another thread, solves this problem too. Just call Thread.Join. http://stackoverflow.com/questions/3641296/create-thread-just-like-if-it-were-a-separated-application-in-c/3642766#3642766 – Hans Passant Sep 23 '10 at 06:44
  • I'm sorry but I don't get how to go from that to Navigating and waiting without leaving the function. – Juan Sep 23 '10 at 07:19
  • Oh, never mind. Didn't read the `Thread.Join` thing. My bad. – Juan Sep 23 '10 at 07:30
  • Isn't it too time consuming to create a new thread every time I need to navigate? And also, is it OK to use a WebBrowser created in the main thread to `Navigate` from the new thread so then the main thread can access the contents of the loaded page? – Juan Sep 23 '10 at 07:48

1 Answers1

2

It isn't the most orthodox approach, but it looks like it should work. Note that you should ensure that the iterator is disposed at some point, for example:

void MoveToNext() {
    if (Enumerator != null)
    {
        if (Enumerator.MoveNext()) Browser.Navigate(Enumerator.Current);
        else {
            Enumerator.Dispose();
            Enumerator = null;
        }
    }
}

(and ideally in the form's Dispose too)

But I wonder if keeping an index in an array is simpler?


public partial class Form1 : Form
{
    BrowserWrapper bw;

    public Form1()
    {
        InitializeComponent();
        bw = new BrowserWrapper(webBrowser1);
    }

    public string[] Get()
    {
        return new[] { "www.google.com", "www.yahoo.com", "www.hotmail.com"};
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bw.LoadPages(Get());
    }
}

public class BrowserWrapper
{
    public void LoadPages(string[] pages) {
        this.pages = pages;
        nextIndex = 0;
        NextPage();
    }
    WebBrowser Browser;

    public BrowserWrapper(WebBrowser browser)
    {
        Browser = browser;
        Browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(Browser_DocumentCompleted);
    }

    void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        if(Browser.ReadyState == WebBrowserReadyState.Complete && e.Url == Browser.Url)
        {  NextPage(); }
    }

    string[] pages;
    int nextIndex;
    void NextPage() {
        if(pages != null && nextIndex < pages.Length) {
            Browser.Navigate(pages[nextIndex++]);
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I was trying to figure out how to use an index in an array to accomplish what I'm looking for (NavigateAndWait) but I give up. Can I get a bit more details? – Juan Sep 23 '10 at 08:27
  • Oh, I got you. Problem with that is that the index is not as dynamic as a method. My `Get()` method was just a simplification. On real life I would do lots of things between calls (between `yield return` s). And I wouldn't necessarily know page 2 until after loading page 1, etc. – Juan Sep 23 '10 at 16:24
  • Actually my method won't work for me either. It's only good if you are using a single `WebBrowser`. – Juan Sep 23 '10 at 16:30