Simulate NavigateAndWait with a WebBrowser using yield return
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 th开发者_开发百科en 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);
}
}
}
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++]);
}
}
}
精彩评论