2010-11-08 3 views
6

J'essaie de trouver un moyen pour que mon programme sache quand un WebBrowser navigue et quand il ne l'est pas. C'est parce que le programme va interagir avec le document chargé via JavaScript qui sera injecté dans le document. Je n'ai aucun autre moyen de savoir quand il commence à naviguer que de gérer l'événement Navigating puisque ce n'est pas mon programme mais l'utilisateur qui va naviguer en interagissant avec le document. Mais alors, quand DocumentCompleted se produit ne signifie pas nécessairement qu'il a fini de naviguer. J'ai été googler beaucoup et trouvé deux pseudo-solutions:Est-il possible de savoir avec certitude si un navigateur Web navigue ou non?

  1. Vérifiez la propriété de WebBrowser ReadyState en cas DocumentCompleted. Le problème avec ceci est que si le document mais pas un cadre dans le document se charge, le ReadyState sera Completed même si le document principal n'est pas terminé.

  2. Pour éviter cela, ils conseillent de voir si le paramètre Url passé à DocumentCompleted correspond à la Url du WebBrowser. De cette façon, je saurais que DocumentCompleted n'est pas invoqué par une autre image dans le document.

Le problème 2 est que, comme je le disais, la seule façon que je dois savoir quand une page est en manipulant est naviguait l'événement Navigating (ou Navigated). Donc, si, par exemple, je suis dans Google Maps et que je clique sur Rechercher, Navigating sera appelée, mais un cadre navigue; pas la page entière (sur le cas spécifique de Google, je pourrais utiliser la propriété TargetFrameName de WebBrowserNavigatingEventArgs pour vérifier si c'est un cadre qui navigue, mais les cadres n'ont pas toujours de noms). Donc après cela, DocumentCompleted sera appelé, mais pas avec le même Url que mon WebBrowser s Url parce que c'était juste un cadre celui qui a navigué, donc mon programme serait qu'il navigue encore, pour toujours.

L'ajout d'appels à Navigating et la soustraction des appels à DocumentCompleted ne fonctionneront pas non plus. Ils ne sont pas toujours les mêmes. Je n'ai pas trouvé de solution à ce problème depuis des mois déjà; J'ai utilisé les solutions 1 et 2 et j'espère qu'elles fonctionneront dans la plupart des cas. Mon plan était d'utiliser une minuterie au cas où une page Web a des erreurs ou quelque chose, mais je ne pense pas que Google Maps a des erreurs. Je pourrais toujours l'utiliser mais la seule solution la plus laide serait de graver mon PC.

Edit: Jusqu'à présent, c'est le plus proche que je dois une solution:

partial class SafeWebBrowser 
{ 
    private class SafeNavigationManager : INotifyPropertyChanged 
    { 
     private SafeWebBrowser Parent; 
     private bool _IsSafeNavigating = false; 
     private int AccumulatedNavigations = 0; 
     private bool NavigatingCalled = false; 

     public event PropertyChangedEventHandler PropertyChanged; 

     public bool IsSafeNavigating 
     { 
      get { return _IsSafeNavigating; } 
      private set { SetIsSafeNavigating(value); } 
     } 

     public SafeNavigationManager(SafeWebBrowser parent) 
     { 
      Parent = parent; 
     } 

     private void SetIsSafeNavigating(bool value) 
     { 
      if (_IsSafeNavigating != value) 
      { 
       _IsSafeNavigating = value; 
       OnPropertyChanged(new PropertyChangedEventArgs("IsSafeNavigating")); 
      } 
     } 

     private void UpdateIsSafeNavigating() 
     { 
      IsSafeNavigating = (AccumulatedNavigations != 0) || (NavigatingCalled == true); 
     } 

     private bool IsMainFrameCompleted(WebBrowserDocumentCompletedEventArgs e) 
     { 
      return Parent.ReadyState == WebBrowserReadyState.Complete && e.Url == Parent.Url; 
     } 

     protected void OnPropertyChanged(PropertyChangedEventArgs e) 
     { 
      if (PropertyChanged != null) PropertyChanged(this, e); 
     } 

     public void OnNavigating(WebBrowserNavigatingEventArgs e) 
     { 
      if (!e.Cancel) NavigatingCalled = true; 
      UpdateIsSafeNavigating(); 
     } 

     public void OnNavigated(WebBrowserNavigatedEventArgs e) 
     { 
      NavigatingCalled = false; 
      AccumulatedNavigations++; 
      UpdateIsSafeNavigating(); 
     } 

     public void OnDocumentCompleted(WebBrowserDocumentCompletedEventArgs e) 
     { 
      NavigatingCalled = false; 
      AccumulatedNavigations--; 
      if (AccumulatedNavigations < 0) AccumulatedNavigations = 0; 
      if (IsMainFrameCompleted(e)) AccumulatedNavigations = 0; 
      UpdateIsSafeNavigating(); 
     } 
    } 
} 

SafeWebBrowserWebBrowser hérite. Les méthodes OnNavigating, OnNavigated et OnDocumentCompleted sont appelées sur les méthodes surchargées WebBrowser. La propriété IsSafeNavigating est celle qui me ferait savoir si elle navigue ou non.

Répondre

1

Non, il n'y a pas de méthode qui fonctionne pour tous les sites Web. Raison: Un Javascript pourrait déclencher une navigation tout à coup (pensez à AJAX ...) et il n'y a aucun moyen de prédire si ou quand cela se produit. Sauf si vous développez pour un site spécifique bien sûr.

Je recommande de poser une question différente: Que se passe-t-il si la navigation a lieu alors que vous voulez faire quelque chose? Une fois que vous savez que vous pouvez attraper l'erreur.

+0

Mais même si JavaScript déclenche une navigation, les événements 'Navigating' et' Navigated' seront appelés. Je l'ai testé et ça a fonctionné de cette façon. Je ne comprends pas votre deuxième paragraphe. Qu'entendez-vous par "pendant que vous voulez faire quelque chose"? – Juan

+0

@jsoldi j'ai peur FrankJK a tort, s'il y a une volonté, il y a un moyen. vous obtenez un repos de 5 secondes après que le document entier a été chargé pour attendre les redirections de JavaScript, ou vérifiez-les après la fin du document (en vérifiant les événements) si un document est déclenché ou non. Je vais (maintenant) poster un message sur la façon dont vous pouvez surmonter certains de vos obstacles. –

0

D'abord, je l'ai converti le document au format XML et ensuite utilisé ma méthode magique:

nodeXML = HtmlToXml.ConvertToXmlDocument((IHTMLDocument2)htmlDoc.DomDocument); 
    if (ExitWait(false)) 
     return false; 
conversion

:

public static XmlNode ConvertToXmlDocument(IHTMLDocument2 doc2) 
{ 
    XmlDocument xmlDoc = new XmlDocument(); 
    IHTMLDOMNode htmlNodeHTML = null; 
    XmlNode xmlNodeHTML = null; 

    try 
    { 
     htmlNodeHTML = (IHTMLDOMNode)((IHTMLDocument3)doc2).documentElement; 
     xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", ""/*((IHTMLDocument2)htmlDoc.DomDocument).charset*/, "")); 
     xmlNodeHTML = xmlDoc.CreateElement("html"); // create root node 
     xmlDoc.AppendChild(xmlNodeHTML); 
     CopyNodes(xmlDoc, xmlNodeHTML, htmlNodeHTML); 
    } 
    catch (Exception err) 
    { 
     Utils.WriteLog(err, "Html2Xml.ConvertToXmlDocument"); 
    } 

méthode magique:

private bool ExitWait(bool bDelay) 
{ 
    if (m_bStopped) 
     return true; 
    if (bDelay) 
    { 
     DateTime now = DateTime.Now; 
     DateTime later = DateTime.Now; 
     TimeSpan difT = (later - now); 
     while (difT.TotalMilliseconds < MainDef.IE_PARSER_DELAY) 
     { 
      Application.DoEvents(); 
      System.Threading.Thread.Sleep(10); 
      later = DateTime.Now; 
      difT = later - now; 
      if (m_bStopped) 
       return true; 
     } 
    } 
    return m_bStopped; 
} 

où m_bStopped est faux Par défaut, IE_PARSER_DELAY est une valeur de délai d'expiration. J'espère que cela aide.

3

Attendre que le document soit chargé est un problème difficile, mais vous voulez vérifier en permanence .ReadyState et .Busy (ne pas oublier cela). Je vais vous donner quelques informations générales dont vous aurez besoin, puis je répondrai à votre question spécifique à la fin.

BTW, NC = NavigateComplete et DC = DocumentComplete. De plus, si la page que vous attendez contient des cadres, vous devez vous y référer et vérifier leur .busy et .readystate, et si les cadres sont imbriqués, les cadres imbriqués .readystate et .busy De même, vous devez écrire une fonction qui récupère récursivement ces références. Maintenant, quel que soit le nombre d'images, le premier événement CN déclenché est toujours le premier document et le dernier événement DC déclenché est toujours celui du premier document (parent).

Alors vous devriez vérifier pour voir si c'est le premier appel et le pDisp Is WebBrowser1.object (littéralement c'est ce que vous tapez dans votre instruction if) alors vous connaissez son NC pour le document de niveau supérieur, alors vous attendez que ce même objet apparaisse dans un événement DC, donc sauvegardez le pDisp dans une variable globale, et attendez qu'un DC soit exécuté et que pDisp de DC soit égal au pDisp global que vous avez enregistré pendant le premier événement NC (comme dans, le pDisp que vous avez sauvegardé globalement dans le premier événement NC qui a tiré). Donc, une fois que vous savez que pDisp a été retourné dans un contrôleur de domaine, vous savez que le chargement du document est terminé. Ceci améliorera votre méthode de curry, cependant, pour la rendre plus infaillible, vous devez aussi vérifier les cadres, car même si vous avez fait tout ce qui précède, il est plus de 90% bon mais pas 100% imbécile preuve, besoin de faire plus pour cela.

Pour réussir le comptage NC/DC d'une manière significative (il est possible, croyez-moi) vous devez enregistrer le pDisp de chaque NC dans un tableau ou une collection, si et seulement si ce n'est déjà fait exister dans ce tableau/collection. La clé pour faire ce travail est de vérifier la duplication NC pDisp, et ne pas l'ajouter si elle existe. Parce que ce qui se passe est, NC déclenche avec une URL particulière, puis une redirection côté serveur ou un changement d'URL se produit et quand cela se produit, le NC est renvoyé, MAIS cela arrive avec le même objet pDisp utilisé pour l'ancienne URL. Ainsi, le même objet pDisp est envoyé au deuxième événement NC qui se produit pour la deuxième fois avec une nouvelle URL mais tout est fait avec exactement le même objet pDisp.Maintenant, parce que vous avez le nombre de tous les objets NC pDisp uniques, vous pouvez (un par un) les supprimer lorsque chaque événement DC se produit, en faisant la comparaison typique If pDisp Is pDispArray(i) Then (ceci est en VB) enveloppé dans une boucle For et pour chaque décollage, votre compte de tableau se rapprochera de 0. C'est la façon précise de le faire, cependant, cela ne suffit pas, car une autre paire NC/DC peut apparaître après que votre compte atteint 0. Aussi, vous devez vous souvenir de faire exactement la même vérification ForPop de boucle For dans l'événement NavigateError comme vous le faites dans l'événement DC, car lorsqu'une erreur de navigation se produit, un événement NavigateError est déclenché au lieu de l'événement DC.

Je sais que c'était beaucoup à prendre, mais il m'a fallu des années pour faire face à ce contrôle redoutable pour comprendre ces choses, j'ai d'autres méthodes de code si vous en avez besoin, mais certaines des choses que je mentionné ici par rapport à la navigation WB étant vraiment prêt, n'a pas été publié en ligne avant, donc j'espère vraiment que vous les trouverez utiles et laissez-moi savoir comment vous allez. Aussi, si vous voulez/besoin d'éclaircissements sur certains de ce faire, laissez-moi savoir, malheureusement, ce qui précède n'est pas tout si vous voulez être sûr à 100% que la page Web est chargée, acclamations. PS: Aussi, j'ai oublié de mentionner, compter sur les URL pour faire n'importe quel type de comptage est inexact et une très mauvaise idée car plusieurs images peuvent avoir la même URL - par exemple, le site www.microsoft.com fait cela , il y a environ 3 images qui appellent le site principal de MS que vous voyez dans la barre d'adresse. N'utilisez pas d'URL pour une méthode de comptage.

Questions connexes