Waiting for WebBrowserNavigatedEvent to fire

O

ofiras

Hi everyone,
I am working on a program in C#, and I have a problem. I have a
WebBrowser control, and I have to make a function that navigates the
WebBrowser to a certain URL, and returns only after the WebBrowser has
navigated. This means I have to make my function wait for the
WebBrowser to finish navigating (when WebBrowserNavigatedEvent fires
it should continue). I realized that what I have to do is using
threading, but I've never used threads so I don't know how.
Please help me,
Ofir.
 
P

Patrice

Hello,

Because ? It's always better to have some context. For example you could
just disable the UI and enable it again on this event if the problem is to
prevent the user to interact with the UI.
 
P

Peter Duniho

ofiras said:
Hi everyone,
I am working on a program in C#, and I have a problem. I have a
WebBrowser control, and I have to make a function that navigates the
WebBrowser to a certain URL, and returns only after the WebBrowser has
navigated. This means I have to make my function wait for the
WebBrowser to finish navigating (when WebBrowserNavigatedEvent fires
it should continue). I realized that what I have to do is using
threading, but I've never used threads so I don't know how.

Define "navigated". The DocumentCompleted event is useful for knowing
when the document has been completely loaded, or you can use the
Navigated event to know when navigation is done, and the actual loading
of the document is about to commence.

Neither of those events require using an additional thread per se. As
Patrice says, depending on your needs, it could be much more appropriate
to simply modify the state of your UI or do something else appropriate,
rather than just starting up a new thread just for the purpose of making
it wait.

Pete
 
O

Omatase

Hi everyone,
I am working on a program in C#, and I have a problem. I have a
WebBrowser control, and I have to make a function that navigates the
WebBrowser to a certain URL, and returns only after the WebBrowser has
navigated. This means I have to make my function wait for the
WebBrowser to finish navigating (when WebBrowserNavigatedEvent fires
it should continue). I realized that what I have to do is using
threading, but I've never used threads so I don't know how.
Please help me,
Ofir.

I haven't used this control before, but it seems as though something
like this would work

public Form1()
{
webBrowser1.ProgressChanged += ((a, b) =>
{
if (((WebBrowserProgressChangedEventArgs)
e).CurrentProgress >=
((WebBrowserProgressChangedEventArgs)
e).MaximumProgress)
{
// loading is complete, execute UI update code
here
}
});
}

private void button1_Click(object sender, EventArgs e)
{
webBrowser1.Url = new Uri("http://www.google.com");
}

In the constructor I tell the webBrowser control to execute some code
when the ProgressChanged event is called. In that event I evaluate
whether or not it's done downloading the requested URI.
 
O

Omatase

I haven't used this control before, but it seems as though something
like this would work

public Form1()
        {
            webBrowser1.ProgressChanged += ((a, b) =>
            {
                if (((WebBrowserProgressChangedEventArgs)
e).CurrentProgress >=
                    ((WebBrowserProgressChangedEventArgs)
e).MaximumProgress)
                {
                    // loading is complete, execute UI update code
here
                }
            });
        }

        private void button1_Click(object sender, EventArgs e)
        {
            webBrowser1.Url = new Uri("http://www.google.com");
        }

In the constructor I tell the webBrowser control to execute some code
when the ProgressChanged event is called. In that event I evaluate
whether or not it's done downloading the requested URI.

This would be even better

webBrowser1.DocumentCompleted += ((a, b) =>
{
// loading is complete, execute UI update code here
});
 
O

ofiras

Define "navigated".  The DocumentCompleted event is useful for knowing
when the document has been completely loaded, or you can use the
Navigated event to know when navigation is done, and the actual loading
of the document is about to commence.

Neither of those events require using an additional thread per se.  As
Patrice says, depending on your needs, it could be much more appropriate
to simply modify the state of your UI or do something else appropriate,
rather than just starting up a new thread just for the purpose of making
it wait.

Pete

I guess I was a bit misunderstood.
I disabled the UI, and this has nothing to do with my problem.
What I need to do is make my function wait until the document is fully
loaded (as Pete said, DocumentCompleted event would fit).
I need this since after navigating to a page, I need to use elements
inside the page. I could do this by using two functions, one that will
navigate the webbrowser to the url I want, than after the
DocumentCompleted event is fired it will invoke another function that
would do the rest.
I prefer, if possible, to use the first option (the function will wait
until the page is loaded) since it is a lot more organized, and I also
might need it in the future so I want to know how to do it.
 
P

Peter Duniho

ofiras said:
[...]
I need this since after navigating to a page, I need to use elements
inside the page. I could do this by using two functions, one that will
navigate the webbrowser to the url I want, than after the
DocumentCompleted event is fired it will invoke another function that
would do the rest.
I prefer, if possible, to use the first option (the function will wait
until the page is loaded) since it is a lot more organized, and I also
might need it in the future so I want to know how to do it.

I disagree that "it is a lot more organized". Event-driven programming
encapsulates functionality, separating the details into small,
easily-understood components. That's a lot more organized than having a
kitchen-sink method to do everything, IMHO.

And of course in this case, your attempt to handle everything in the
same method will only complicate things, because in waiting you will be
blocking the only thread in your process that is involved in updating
the UI. The alternative would be to create a new thread, from which you
have to call Invoke() to set the new URL to navigate to, and wait on
some synchronization object (e.g. using Monitor with an object instance,
or a WaitHandle instnace, etc.) and then use Invoke() again to access
the WebControl object again for your processing. Which is even more
complicated than just blocking the UI thread (which is in and of itself
complicated enough and problematic).

I would say that separating the initiation of the navigation and the
subsequent processing after the document has been completely loaded into
two different sections of code, using the DocumentCompleted event to
signal the execution of the latter, is in fact the simplest,
best-organized approach.

Pete
 
O

Omatase

ofiras said:
[...]
I need this since after navigating to a page, I need to use elements
inside the page. I could do this by using two functions, one that will
navigate the webbrowser to the url I want, than after the
DocumentCompleted event is fired it will invoke another function that
would do the rest.
I prefer, if possible, to use the first option (the function will wait
until the page is loaded) since it is a lot more organized, and I also
might need it in the future so I want to know how to do it.

I disagree that "it is a lot more organized".  Event-driven programming
encapsulates functionality, separating the details into small,
easily-understood components.  That's a lot more organized than having a
kitchen-sink method to do everything, IMHO.

And of course in this case, your attempt to handle everything in the
same method will only complicate things, because in waiting you will be
blocking the only thread in your process that is involved in updating
the UI.  The alternative would be to create a new thread, from which you
have to call Invoke() to set the new URL to navigate to, and wait on
some synchronization object (e.g. using Monitor with an object instance,
or a WaitHandle instnace, etc.) and then use Invoke() again to access
the WebControl object again for your processing.  Which is even more
complicated than just blocking the UI thread (which is in and of itself
complicated enough and problematic).

I would say that separating the initiation of the navigation and the
subsequent processing after the document has been completely loaded into
two different sections of code, using the DocumentCompleted event to
signal the execution of the latter, is in fact the simplest,
best-organized approach.

Pete

I agree, if you are going to have to have an asynchronous call, it's
best to make the rest of your code that touches this friendly to that.
Just put your code that has to execute after the page is done loading
into a delegate. That encapsulates it nicely.
 
O

ofiras

Well, I didn't really meant that all of my code would be in the same
function. This function would call other function.
I've tried doing it by navigating, adding DocumentCompleate event, and
when the event fires it calls another function that dose what it needs
to do. This is easy when it happens once, but I have to do several
things, and this makes it all messy. This way I have a lot of
functions and events, and it is hard to see the connections between
them.
If I could make a function that navigates to a certain url, waits
until loading is complete and only then returns, I could use for all
of my needs, and I believe it would be much simpler.
This is how it should look:


private void Navigate_To_Url(string url)
{
webBrowser1.Navigate(url);
// Here is where the waiting for the DocumentCompleate
event would be
return;
}

Since using:

while (webBrowser1.ReadyState != WebBrowserReadyState.Complete ) ;

would stop the webBrowser from loading, i guess I have to use
threading.
If there is a way doing it, I would be thankful if you can write as
code (I understand it better since english, as you might have noticed,
is not my mother tongue).
Sorry if I haven't understood it correctly, and I really rather use
events and functions. I am not a professional, but still it looks to
me pretty obvious that waiting for the event would be better.
Thanks,
Ofir.
 
P

Peter Duniho

ofiras said:
Well, I didn't really meant that all of my code would be in the same
function. This function would call other function.

You still have the complication of synchronization and coordination.
Refactoring code into multiple methods is the least of your worries.
I've tried doing it by navigating, adding DocumentCompleate event, and
when the event fires it calls another function that dose what it needs
to do. This is easy when it happens once, but I have to do several
things, and this makes it all messy. This way I have a lot of
functions and events, and it is hard to see the connections between
them.

Your code should not have "connections between them". That's the point
of encapsulation. So that one section of code can do its job without
worrying about what's going on in a separate section. Coordination
should happen only in specific, narrow bottlenecks, specifically so that
the connections between objects are kept simple and manageable.
If I could make a function that navigates to a certain url, waits
until loading is complete and only then returns, I could use for all
of my needs, and I believe it would be much simpler.

Unfortunately, that's just not how it would work out. Such a function
would introduce an unnecessary dependency between pieces of code that
really shouldn't need to depend on each other, would interfere with the
normal flow of execution in your main GUI thread, and will be overly
complicated to implement on top of all that (assuming you can get it to
work at all, which is not a foregone conclusion, given the WebBrowser
control's own ties to the main GUI thread).
This is how it should look:


private void Navigate_To_Url(string url)
{
webBrowser1.Navigate(url);
// Here is where the waiting for the DocumentCompleate
event would be
return;
}

Since using:

while (webBrowser1.ReadyState != WebBrowserReadyState.Complete ) ;

would stop the webBrowser from loading, i guess I have to use
threading.

_Anything_ that blocks that main GUI thread will have the same effect.
That's what I'm alluding to above. If you block that main GUI thread,
then window messages involved in the normal processing of things simply
don't get handled, causing the UI to just come to a halt.
If there is a way doing it, I would be thankful if you can write as
code (I understand it better since english, as you might have noticed,
is not my mother tongue).
Sorry if I haven't understood it correctly, and I really rather use
events and functions. I am not a professional, but still it looks to
me pretty obvious that waiting for the event would be better.

It's unfortunate that seems obvious to you, because all that means is
that your intuition is flawed.

Here's a short snippet that hopefully illustrates how your code really
should look:

class Form1 : Form
{
public Form1()
{
InitializeComponent();

// This could also be configured in the Designer,
// so that the InitializeComponent() method handles
// the initialization instead
webBrowser1.DocumentCompleted +=
webBrowser1_DocumentCompleted;
}

private void Navigate_To_Url(string url)
{
webBrowser1.Navigate(url);
}

private void webBrowser1_DocumentCompleted(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
// do your processing here
}
}

If you have specific per-document state you need to deal with, then of
course you have to manage that. But anything that would properly avoid
blocking the main GUI thread would result in exactly that same issue
anyway. The most common approach is to simply disable the UI that would
allow the user to initiate another URL navigation, while the current
navigation is taking place, to ensure that only one can take place at a
time. But if you feel you need it, there are techniques you could use
to allow multiple navigations to take place simultaneously (assuming you
have multiple instances of the WebBrowser control in which each
navigation can take place, of course).

Pete
 
P

Patrice

Have you seen Omatase response 2 ? It allows perhaps to be closer from the
style you wish but avoid fighting the event driven paragdim (basically
Omatase shows a way to code the event content at the caller site using the
=> notation but not sure if you are familiar with that).

If its add more confusion then I would strongly suggest to restart with a
fresh mind and see if your code could be organized better. I'm afraid that
you can"t go against that as fighting against the event driven paradigm will
never work as a general solution but only in very particular cases (unless
in simplest cases you don't even known what is the next event that will take
place until it actually happens).

Have you learned about classes ? Do you have a fair amount of code inside
your events ? For a start you could avoid that and only have *calls* inside
your events. For example this way you can have a scheme such as:


OnThisEvent
MyProcessingClass.DoThis

On SomeOtherEvent
MyProcessingClass.DoThat

etc..

Basically try to keep your UI code very simple and on one side and all of
your processing code on another side...
 
O

ofiras

Have you seen Omatase response 2 ? It allows perhaps to be closer from the
style you wish but avoid fighting the event driven paragdim (basically
Omatase shows a way to code the event content at the caller site using the
=> notation but not sure if you are familiar with that).

If its add more confusion then I would strongly suggest to restart with a
fresh mind and see if your code could be organized better. I'm afraid that
you can"t go against that as fighting against the event driven paradigm will
never work as a general solution but only in very particular cases (unless
in simplest cases you don't even known what is the next event that will take
place until it actually happens).

Have you learned about classes ? Do you have a fair amount of code inside
your events ? For a start you could avoid that and only have *calls* inside
your events. For example  this way you can have a scheme such as:

OnThisEvent
    MyProcessingClass.DoThis

On SomeOtherEvent
    MyProcessingClass.DoThat

etc..

Basically try to keep your UI code very simple and on one side and all of
your processing code on another side...

Thank you both, Pete and Patrice. I actually did like Pete suggest,
and this looks pretty messy for me.
Anyway, I won't argue with you two. Since I am sure you are much
better than me in C#. I would try using a different class for my
events of functions as Patrice suggested.
Thank you very much,
Ofir.
 
P

Peter Duniho

ofiras said:
Thank you both, Pete and Patrice. I actually did like Pete suggest,
and this looks pretty messy for me. [...]

Don't take our word for it if it's not readily apparent to you.
Implement it the other way and see which leads to fewer headaches. :)

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top