63

How can I detect when a System.Windows.Forms.WebBrowser control has completed loading?

I tried to use the Navigate and DocumentCompleted events but both of them were raised a few times during document loading!

Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74
Neir0
  • 12,849
  • 28
  • 83
  • 139
  • How are you using c#? Is this an asp.net question, or are you using the web browser control? – kibibu May 06 '10 at 01:42
  • @kibibu i am using the WebBrowser control. – Neir0 May 06 '10 at 01:44
  • Comparing the AbsolutePath may not work if the page has many frames. I tried that way before but haven't succeeded yet. I'm still stuck with this problem. :( :( –  Nov 27 '10 at 03:42

9 Answers9

37

I think the DocumentCompleted event will get fired for all child documents that are loaded as well (like JS and CSS, for example). You could look at the WebBrowserDocumentCompletedEventArgs in DocumentCompleted and check the Url property and compare that to the Url of the main page.

Omar
  • 16,329
  • 10
  • 48
  • 66
Paul Kearney - pk
  • 5,435
  • 26
  • 28
  • That's what I figured out from fighting this problem myself. I was getting a bunch of extra completions on things like ad banners. I discard any event that doesn't match the URL I'm after and it works. – Loren Pechtel May 06 '10 at 02:38
  • 6
    @Loren Pechtel i'm affraid to say there are 3 problems with this approach. first, when ur parent (top level) url does a server side url redirect, the url of the document u r trying to compare to changes. secondly, there's the situation of a frame and the parent page having the same url which change behaviour on other parameters like referrer etc so url is same. also, client-side redirects - they occur AFTER you think the page has loaded and webbrowser control returns "all is good to go". if you want help on how to reduce these, let me know & i can provide an answer here. – Erx_VB.NExT.Coder Feb 01 '12 at 12:24
  • @Paul Kearney - pk feel free to read my last comment to Loren. – Erx_VB.NExT.Coder Feb 01 '12 at 12:27
  • Shooting from the hip - but i would bet money on scripts not firing documentloaded - as they do not have a DOMDocument 'attached'. It is probably frames, since a html segment that contains framesets technically have windows embedded in windows - thus multiple documents – mschr Jul 25 '16 at 14:01
31

I did the following:

void BrowserDocumentCompleted(object sender,
        WebBrowserDocumentCompletedEventArgs e)
{
  if (e.Url.AbsolutePath != (sender as WebBrowser).Url.AbsolutePath)
    return; 

  //The page is finished loading 
}

The last page loaded tends to be the one navigated to, so this should work.

From here.

Community
  • 1
  • 1
Kyle Rosendo
  • 25,001
  • 7
  • 80
  • 118
  • Perfect - just what I needed. Explains why sometimes the whole page was appearing, and sometimes only the partial page. – Elie Dec 28 '12 at 04:08
  • Just change it to ((WebBrowser)sender).Url ;-) – juFo Apr 09 '13 at 14:48
  • 1
    Not really work. The problem is that there are facebook and twitter content, and the main problem is if the page load content via js with ajax. Everything in the documentReady event. – Péter Jun 18 '13 at 09:35
  • How to call it? I tried call it `BrowserDocumentCompleted(sender, e);` – Ave Jun 29 '16 at 03:48
  • @vanloc The name doesn't matter. You just need to assign it to the DocumentCompleted event of the WebBrowser control. – Tom Lint Jan 13 '17 at 13:59
19

The following should work.

private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    //Check if page is fully loaded or not
    if (this.webBrowser1.ReadyState != WebBrowserReadyState.Complete)
        return;
    else
        //Action to be taken on page loading completion
}
ePandit
  • 2,905
  • 2
  • 24
  • 15
  • Quite helpful & simple! – Joe DF Jul 02 '14 at 22:52
  • 1
    In my application, the event is fired at least four times – Jack Griffin May 06 '15 at 15:55
  • @JackGriffin That's because every iframe is also a document and hence will fire the event. It's tricky to do this. You could check Urls as mentioned by Paul in the answer above, but this can also fail, as described by Erx_VB.NExT.Coder. If URL's that are to be opened fall within a certain predefined URL set, you might know how to avoid these. For example, when i first faced this problem, I knew what URLs will be opened so I created a child class of WebBrowser class and added some additional information to it. Then, it was easy ( by prompting the sender object) to do this. – Transcendental Jun 08 '15 at 14:59
  • @Transcendental I see the problem. Kyle's solution worked for me, but maybe I've been just lucky :-). – Jack Griffin Jul 12 '15 at 17:05
  • Me too. My app fires every two times. – JHK Sep 05 '21 at 10:08
13

Note the url in DocumentCompleted can be different than navigating url due to server transfer or url normalization (e.g. you navigate to www.microsoft.com and got http://www.microsoft.com in documentcomplete)

In pages with no frames, this event fires one time after loading is complete. In pages with multiple frames, this event fires for each navigating frame (note navigation is supported inside a frame, for instance clicking a link in a frame could navigate the frame to another page). The highest level navigating frame, which may or may not be the top level browser, fires the final DocumentComplete event.

In native code you would compare the sender of the DocumentComplete event to determine if the event is the final event in the navigation or not. However in Windows Forms the sender parameter is not wrapped by WebBrowserDocumentCompletedEventArgs. You can either sink the native event to get the parameter's value, or check the readystate property of the browser or frame documents in the DocumentCompleted event handler to see if all frames are in the ready state.

There is a prolblem with the readystate method as if a download manager is present and the navigation is to a downloadable file, the navigation could be cancelled by the download manager and the readystate won't become complete.

Sheng Jiang 蒋晟
  • 15,125
  • 2
  • 28
  • 46
6

I had the same issue of multiple DocumentCompleted fired events and tried out all the suggestions above. Finally, seems that in my case neither IsBusy property works right nor Url property, but the ReadyState seems to be what I needed, because it has the status 'Interactive' while loading the multiple frames and it gets the status 'Complete' only after loading the last one. Thus, I know when the page is fully loaded with all its components.

I hope this may help others too :)

Shoe
  • 74,840
  • 36
  • 166
  • 272
iArv
  • 61
  • 1
  • 1
3

It doesn't seem to trigger DocumentCompleted/Navigated events for external Javascript or CSS files, but it will for iframes. As PK says, compare the WebBrowserDocumentCompletedEventArgs.Url property (I don't have the karma to make a comment yet).

Joel Rein
  • 3,608
  • 1
  • 26
  • 32
3

If you're using WPF there is a LoadCompleted event.

If it's Windows.Forms, the DocumentCompleted event should be the correct one. If the page you're loading has frames, your WebBrowser control will fire the DocumentCompleted event for each frame (see here for more details). I would suggest checking the IsBusy property each time the event is fired and if it is false then your page is fully done loading.

alimbada
  • 1,392
  • 1
  • 12
  • 27
0

Using the DocumentCompleted event with a page with multiple nested frames didn't work for me.

I used the Interop.SHDocVW library to cast the WebBrowser control like this:

public class webControlWrapper
{
    private bool _complete;
    private WebBrowser _webBrowserControl;

    public webControlWrapper(WebBrowser webBrowserControl)
    {
        _webBrowserControl = webBrowserControl;
    }

    public void NavigateAndWaitForComplete(string url)
    {
        _complete = false;

        _webBrowserControl.Navigate(url);

        var webBrowser = (SHDocVw.WebBrowser) _webBrowserControl.ActiveXInstance;

        if (webBrowser != null)
            webBrowser.DocumentComplete += WebControl_DocumentComplete;

        //Wait until page is complete
        while (!_complete)
        {
            Application.DoEvents();
        }
    }

    private void WebControl_DocumentComplete(object pDisp, ref object URL)
    {
        // Test if it's the main frame who called the event.
        if (pDisp == _webBrowserControl.ActiveXInstance)
            _complete = true;
    }

This code works for me when navigating to a defined URL using the webBrowserControl.Navigate(url) method, but I don't know how to control page complete when a html button is clicked using the htmlElement.InvokeMember("click").

Frank_FC
  • 76
  • 1
  • 8
0

You can use the event ProgressChanged ; the last time it is raised will indicate that the document is fully rendered:

this.webBrowser.ProgressChanged += new
WebBrowserProgressChangedEventHandler(webBrowser_ProgressChanged);
Vincent Lidou
  • 201
  • 2
  • 3