web-dev-qa-db-ja.com

C#でWebBrowserコントロールのDocumentCompletedイベントを使用するにはどうすればよいですか?

この質問を書き始める前に、私は以下を解決しようとしていました

// 1. navigate to page
// 2. wait until page is downloaded
// 3. read and write some data from/to iframe 
// 4. submit (post) form

問題は、iframeがWebページに存在する場合、DocumentCompletedイベントが複数回発生することでした(各ドキュメントが完了した後)。プログラムが、完了していないDOMからデータを読み取ろうとした可能性が高く、当然失敗しました。

しかし、この質問を書いているときに突然 'What if' monsterインスピレーションを得て、問題を修正しました。解決しようとしています。これをグーグルで検索できなかったので、ここに投稿するといいと思いました。

    private int iframe_counter = 1; // needs to be 1, to pass DCF test
    public bool isLazyMan = default(bool);

    /// <summary>
    /// LOCK to stop inspecting DOM before DCF
    /// </summary>
    public void waitPolice() {
        while (isLazyMan) Application.DoEvents();
    }

    private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
        if(!e.TargetFrameName.Equals(""))
            iframe_counter --;
        isLazyMan = true;
    }

    private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
        if (!((WebBrowser)sender).Document.Url.Equals(e.Url))
            iframe_counter++;
        if (((WebBrowser)sender).Document.Window.Frames.Count <= iframe_counter) {//DCF test
            DocumentCompletedFully((WebBrowser)sender,e);
            isLazyMan = false; 
        }
    }

    private void DocumentCompletedFully(WebBrowser sender, WebBrowserDocumentCompletedEventArgs e){
        //code here
    }

少なくとも今のところ、私の5mのハックはうまく機能しているようです。

たぶん私はグーグルやMSDNのクエリに本当に失敗していますが、「C#でwebbrowserコントロールDocumentCompletedイベントを使用する方法は?」が見つかりません。

備考:ウェブコントロールについて多くを学んだ後、私はそれがFuNKYのものをすることを発見しました。

ドキュメントが完成したことを検出したとしても、ほとんどの場合、そのドキュメントは永遠にそのようにとどまりません。ページの更新は、フレームの更新、リクエストのようなajax、サーバー側のプッシュなど、いくつかの方法で実行できます(非同期通信をサポートし、htmlまたはJavaScriptの相互運用機能を備えたコントロールが必要です)。また、一部のiframeは読み込まれないため、永久に待つことはお勧めできません。

私は最終的に以下を使用しました:

if (e.Url != wb.Url)
14
Margus

AJAX呼び出しも知りたいと思うかもしれません。

これの使用を検討してください:

private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    string url = e.Url.ToString();
    if (!(url.StartsWith("http://") || url.StartsWith("https://")))
    {
            // in AJAX
    }

    if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
    {
            // IFRAME 
    }
    else
    {
            // REAL DOCUMENT COMPLETE
    }
}
14
Yuki

私はまだこの問題の実用的な解決策をオンラインで見つけていません。うまくいけば、これがトップになり、私がそれを解決するために費やした数ヶ月の微調整とそれに関連するエッジケースをすべての人に節約するでしょう。 MicrosoftがisBusyとdocument.readystateの実装/信頼性を変更したため、私はこの問題を何年にもわたって争ってきました。 IE8では、次の解決策に頼らざるを得ませんでした。いくつかの例外を除いて、Margusからの質問/回答に似ています。私のコードは、ネストされたフレーム、javascript/ajaxリクエスト、メタリダイレクトを処理します。わかりやすくするためにコードを簡略化しましたが、タイムアウト関数(含まれていません)を使用して、5分後にdomAccessがfalseに等しい場合にWebページをリセットします。

private void m_WebBrowser_BeforeNavigate(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
{
    //Javascript Events Trigger a Before Navigate Twice, but the first event 
    //will contain javascript: in the URL so we can ignore it.
    if (!URL.ToString().ToUpper().StartsWith("JAVASCRIPT:"))
    {
        //indicate the dom is not available
        this.domAccess = false;
        this.activeRequests.Add(URL);
    }
}

private void m_WebBrowser_DocumentComplete(object pDisp, ref object URL) 
{

    this.activeRequests.RemoveAt(0);

    //if pDisp Matches the main activex instance then we are done.
    if (pDisp.Equals((SHDocVw.WebBrowser)m_WebBrowser.ActiveXInstance)) 
    {
        //Top Window has finished rendering 
        //Since it will always render last, clear the active requests.
        //This solves Meta Redirects causing out of sync request counts
        this.activeRequests.Clear();
    }
    else if (m_WebBrowser.Document != null)
    {
        //Some iframe completed dom render
    }

    //Record the final complete URL for reference
    if (this.activeRequests.Count == 0)
    {
        //Finished downloading page - dom access ready
        this.domAccess = true;
    }
}
3
Marcus Pope

Thorstenとは異なり、ShDocVwを使用する必要はありませんでしたが、ReadyStateをチェックするループを追加し、準備ができていないときにApplication.DoEvents()を使用することが違いました。これが私のコードです:

        this.webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
        foreach (var item in this.urlList) // This is a Dictionary<string, string>
        {
            this.webBrowser.Navigate(item.Value);
            while (this.webBrowser1.ReadyState != WebBrowserReadyState.Complete)
            {
                Application.DoEvents();
            }
        }

そして、WebBrowser_DocumentCompletedの結果をチェックするためにYukiのソリューションを使用しましたが、最後のif/elseはユーザーのコメントごとに交換されました。

     private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        string url = e.Url.ToString();
        var browser = (WebBrowser)sender;

        if (!(url.StartsWith("http://") || url.StartsWith("https://")))     
        {             
            // in AJAX     
        }
        if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)     
        {
            // IFRAME           
        }     
        else     
        {             
            // REAL DOCUMENT COMPLETE
            // Put my code here
        }
    }

魅力のように働いた:)

2
FeiBao 飞豹

私は同じようなことをしなければなりませんでした。私がしていることは、ShDocVwを直接使用することです(プロジェクトに必要なすべての相互運用機能アセンブリへの参照を追加します)。次に、WebBrowserコントロールをフォームに追加しませんが、AXShDocVw.AxWebBrowserコントロールを追加します。

ナビゲートして待機するには、次の方法を使用します。

private void GotoUrlAndWait(AxWebBrowser wb, string url)
{
    object dummy = null;
    wb.Navigate(url, ref dummy, ref dummy, ref dummy, ref dummy);

    // Wait for the control the be initialized and ready.
    while (wb.ReadyState != SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
        Application.DoEvents();
}
0