Fragen zu WebBrowserReadyState.Complete

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Kaktus-Stecher.

    Fragen zu WebBrowserReadyState.Complete

    Hallo Community!

    Bin dabei, mich bisschen in die Materie einzuarbeiten. Habe auch dank des Forums viel dazu lernen können.
    Nur komme ich bei einer Problematik nicht weiter. (auch nicht mit Sufu und google :) )

    Und zwar geht es um folgendes :

    VB.NET-Quellcode

    1. WebBrowser1.Navigate(TextBox1.Text)
    2. Do While WebBrowser1.ReadyState <> WebBrowserReadyState.Complete
    3. Application.DoEvents()
    4. Loop
    5. WebBrowser1.Document.GetElementById("test").InvokeMember("click")


    Habe aber hier im Forum des öfteren gelesen, dass "Application.DoEvents()" keine saubere Form des Programmierens ist.

    Daher habe ich folgenden Code versucht:

    VB.NET-Quellcode

    1. WebBrowser1.Navigate(TextBox1.Text)
    2. If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
    3. WebBrowser1.Document.GetElementById("test").InvokeMember("click")
    4. End If


    Bei beiden soll erst komplett die Seite geladen werden, bevor geklickt wird.
    Für mich ist dieser Code logisch, aber es will einfach net klappen.
    Kann mir da bitte jemand weiter helfen?
    Danke :)
    Nun logisch ist er, aber der Code wir innerhalb von Milli oder Nano Sekunden ausgeführt, heißt also die Seite wird annavigiert und dann geprüft ob der WebBrowser fertig geladen ist und jetzt erkennst du sicher selbst das dies nicht funktionieren kann da eine Seite schon einige Sekunden braucht zum laden.

    Deswegen ist wie im ersten Code eine Schleife die solange läuft bis das Dokument geladen wurde, damit das Programm nicht hängt kann man das DoEvents() nutzen aber ist in der Tat nicht schön.

    Hierbei gibt es 2 Möglichkeiten, zum einen die Schleife in einen BackgroundWorker packen, was aber fürn Anfänger auch schwierig wird da man hierbei dann mit Delegates arbeiten muss.

    Zweite und definitv sinnvollere Möglichkeit ist einfach das DocumentComplete Event des WebBrowsers zu nutzen, dieses wird ausgeführt wenn das Dokument geladen ist.

    VB.NET-Quellcode

    1. Private Sub DocumentComplete() Handles WebBrowser1.DocumentComplete


    so oder so ähnlich.
    Vielen Dank für die Antworten!
    Gugi, wie gesagt kenn ich bisher nur die Sachen, die ich aus dem Forum gefunden habe. :)
    Daher sagt mir navigated leider (noch) nichts. Aber das ist ja schonmal ein Ansatz, wo ich dann wieder suchen kann.

    @Dodo: Das DocumentComplete Event benutze ich bereits schon einmal. Daher musste ich ja nach anderen Wegen suchen. Kann ich das Event denn mehrmals verwenden?

    Im Beispiel ausgedrückt:
    Auf Webseite navigieren
    Warten, bis Seite vollständig geladen ist.
    Aktionen durchführen.
    Dann auf zweite Webseite navigieren (als eingeloggter Benutzer)
    Und hier nochmal warten, bis Seite vollständig geladen ist. (und hier ist auch mein Problem :) )

    Danke,
    Kaktus
    das navigated event ist dieses:

    VB.NET-Quellcode

    1. Private Sub WebBrowser1_Navigated(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserNavigatedEventArgs) Handles WebBrowser1.Navigated
    2. End Sub

    du musst nur mehr den code da rein geben, der ausgeführt werden soll, wenn die seite fertig geladen ist.

    lg Gugi
    Aber Navigated ist sicher nicht das selbe wie Complete, bei Complete ist das Dokument definitiv fertig geladen und du kannst auf Elemente zugreifen, nur weil ermit Navigieren fertig ist heißt ja nicht dass das Dokument da ist.

    Ansonsten klar kannste das Event öfters nutzen, habe ich auch schon öfters gemacht

    VB.NET-Quellcode

    1. Private Sub Button1_Clicked () Handles Button1.Clicked
    2. WebBrowser1.Navigate("Seite")
    3. AddHandler WebBrowser1.DocumentComplete, AddressOf(Complete1)
    4. End Sub
    5. Private Sub Complete1()
    6. ' Seite zum ersten Mal neugeladen
    7. RemoveHandler WebBrowser1.DocumentComplete, AddressOf(Complete1)
    8. WebBrowser1.Navigate("Seite2")
    9. AddHandler WebBrowser1.DocumentComplete, AddressOf(Complete2)
    10. End Sub
    11. Private Sub Complete2()
    12. ' Nachdem die Seite neu geladen wurde
    13. End Sub


    So kannste den Handler beliebig weitergeben und jedesmal anderen Code ausführen.
    Mit der Antwort von Gugi hat es geklappt. Sieht bei mir dann folgendermaßen aus:

    VB.NET-Quellcode

    1. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    2. WebBrowser1.Navigate("Seite2")
    3. End Sub
    4. Private Sub WebBrowser1_Navigated(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserNavigatedEventArgs) Handles WebBrowser1.Navigated
    5. WebBrowser1.Document.GetElementById("ID").InvokeMember("click")
    6. End Sub


    Aber ich kann doch net nach jeder neuen Seite einmal

    VB.NET-Quellcode

    1. Private Sub WebBrowser1_Navigated(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserNavigatedEventArgs) Handles WebBrowser1.Navigated
    2. End Sub


    anwenden?

    Dodo, bei AddressOf(Complete1) kommt der Fehler, dass Complete1 nicht deklariert wurde. Muss ich dies zu Beginn als irgendeine Variable definieren?

    Danke für die Antworten :)
    das navigated event, wird jedes mal angewendet wenn eine seite geladen ist, wenn du aber z.b .schreibst

    VB.NET-Quellcode

    1. WebBrowser1.Document.GetElementById("ID").InvokeMember("click")

    aber kein feld namens ID vorhanden ist, kommt ein error. deswegen würde ich einen try-catch block benutzen.

    lg Gugi
    edit:// 666 beiträge :P

    Kaktus-Stecher schrieb:

    Habe aber hier im Forum des öfteren gelesen, dass "Application.DoEvents()" keine saubere Form des Programmierens ist.

    Ich habe jetzt extra mal gewartet was für Antworten so eintreffen, um mal zu sehen ob irgendjemand darauf eingeht warum der landläufigen Meinung entsprechend DoEvents überhaupt so verpönt ist. Oft sind es ja gerade die frisch Konvertierten, die sich ein paar Regeln angeeignet haben um sie dann allen Neulingen als das 11.Gebot um die Ohren zu hauen.

    Warum Du auf den WebBrowser warten musst, hat Dir Dodo ja schon erklärt: mit dem Navigate "Befehl", wird das Navigieren zwar angestossen, aber es wird Dein Code nicht angehalten (blockiert) bis die Ausführung erledigt ist. Also musst Du selber etwas unternehmen um das Laden des Dokumentes abzuwarten:

    1) Methode "ins Document-Completed Event packen"
    Klar funktioniert das, aber leider nur einmal : wenn Du weiter navigieren willst ist das Deine Methode mit dem "handles Document.completed" schon besetzt und Du stehst auf dem Schlauch
    2) der erwähnte Backgroundworker
    also einen anderen Thread aufmachen (mit ziemlichem Overhead) ist hier ziemlicher Unsinn und macht dazu Deinen Code nicht unbedingt lesbarer
    3) je nach Code-Fortschritt das Document-Completed Event in einer anderen Prozedur abfangen
    ist vermutlich die sauberste Methode wie von Dodo beschrieben. Du gibst mit AddHandler jeweils die Adresse mit die je nach Deinem Code-Fortschritt gerade weitermachen soll sobald das Dokument geladen wurde.
    4) in einer While-Schleife den ReadyState abfragen + das "verpönte" DoEvents
    Ist für mich durchaus eine Alternative, solange Du Dir bewusst bist was(!) Du damit bewirkst: Du gibts in der While-Schleife mit DoEvents immer wieder der Form die Möglichkeit andere Ereignisse abzuarbeiten, z.B. das Ziehen oder Vergrössern der Form, was ja auch durchaus erwünscht ist. Allerdings kannst Du natürlich auch einen Button anklicken (wenn Du schnell genug bist) und anderer Code wird ausgeführt, somit passiert also evtl paralell etwas was Dir nicht bekannt ist un kann zu Problemen führen. Gilt allerdings auch für die AddHandler Methode und alle anderen auch.

    Ich würde für kurze Warteschleifen durchaus die DoEvents Methode verwenden, lange Hintergrundarbeiten (z.B. Bearbeitung von Images) werden dafür in einen anderen Thread ausgelagert. Letztlich musst Du selber entscheiden was für Dich sinnvoller erscheint.

    Kangaroo schrieb:

    Klar funktioniert das, aber leider nur einmal

    quatsch
    Entweder entscheidet man anhand der Url (e.Url!) oder man führt intern einfach einen "Status"

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private WithEvents wb As New WebBrowser
    3. Private Enum BrowserStatus
    4. MacheWas
    5. MachWasAnderes
    6. MachWasGanzAnderes
    7. End Enum
    8. Private CurrentStatus As BrowserStatus = BrowserStatus.MacheWas
    9. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    10. wb.Navigate(New System.Uri("http://www.vb-paradise.de"))
    11. End Sub
    12. Private Sub wb_DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles wb.DocumentCompleted
    13. ' Entscheide anhand der URL
    14. If e.Url.ToString = "http://www.vb-paradise.de" Then
    15. ' machwas
    16. Else
    17. ' mach irgendwas anderes
    18. End If
    19. Select Case CurrentStatus
    20. Case BrowserStatus.MacheWas
    21. ' machwas
    22. CurrentStatus = BrowserStatus.MachWasAnderes
    23. Case BrowserStatus.MachWasAnderes
    24. ' mach was anderes
    25. CurrentStatus = BrowserStatus.MachWasGanzAnderes
    26. Case BrowserStatus.MachWasGanzAnderes
    27. ' mach was ganz anderes
    28. End Select
    29. End Sub
    30. End Class
    Klar funktioniert das so, hatte ich zuerst auch gemacht, aber das wurde dann irgendwann zu unübersichtlich und ich habe das mit Handler weiter geben gemacht, ist für die übersicht aufjedenfall besser, selbst wenn ich bei den Cases auch eine Sub aufrufen könnte.
    Ich habe ja nur 5 einzelne Subs wo in den ersten beiden Zeilen der Handler der jetzigen Sub entzogen wird und auf die nächste gesetzt wird, danach wird der entsprechende Code abgearbeitet, da alle Subs untereinander stehen hat man eine prima übersicht, als ob anstatt der Subs überall While ReadyState Schleifen zwischen wären.

    Aber sicher hat da jeder seine eigene Vorstellung von Übersichtlichkeit.

    picoflop schrieb:

    quatsch
    Entweder entscheidet man anhand der Url (e.Url!) oder man führt intern einfach einen "Status"

    Auch möglich , war aber eigentlich nicht der Sinn meines Beitrags. Ich kann natürlich Stati führen, diese werden aber schnell unübersichtlich und damit fehlerträchtig wenn man sie nicht sehr sorgfältig durchdenkt und durchführt.

    Ein DoEvents ist bestimmt ein Bruch von stringentem OOP aber oft transparenter für die Ablauflogik. Ich habe nur Probleme mit so einseitigen Aussagen "das ist gut" und das ist verpönt. Eine Datenbank zu normalisieren ist zweifellos auch richtig, aber kann (und muss gelegentlich) durchbrochen werden, wenn man sich der Folgen bewusst ist und die Probleme in Kauf nimmt.

    Aber ich schlage vor wir gehen BTT, sonst geraten wir in einen Grundlagenstreit der besser in einem anderen Thread (link vergessen) ausgetragen wird.
    Vielen Dank für die ausführlichen Antworten!
    Mir geht es in erster Linie darum, dass ich verstehe, wie das ganze funktioniert und natürlich, dass mein Programm im Endeffekt das tut, was ich möchte.
    Mit eurer Hilfe tut mein Programm zur Zeit auch das, was ich möchte. Ob ich nachher irgendwann Fehler mit DoEvents() feststellen werde,sei mal dahin gestellt.