Is it possible to know for sure whether WebBrowser is moving or not?

I am trying to find a way for my program to know when WebBrowser is moving and when not. This is due to the fact that the program will interact with the downloaded document through JavaScript, which will be entered into the document. I have no other way of knowing when it will start navigation than Navigating processing, since this is not my program, but a user who will move around interacting with the document. But then, when DocumentCompleted happens, it doesn’t necessarily mean that it has finished navigation. I walked a lot and found two pseudo-solutions:

  • Check the ReadyState property for the DocumentCompleted web browser. The problem is that if the document, but the frame in the document is loading, ReadyState will be Completed , even if the main document is not completed.

  • To prevent this, they are advised to find out if the Url parameter matches the DocumentCompleted Url WebBrowser . That way, I would know that DocumentCompleted not called by any other frame in the document.

Problem with 2 is that, as I said, the only way I should know when navigating a page is to handle the Navigating (or Navigated ) event. So, if, for example, I’m on Google Maps and click “Search”, Navigating will be called, but only the frame moves; not the whole page (in a specific case of Google, I could use the TargetFrameName WebBrowserNavigatingEventArgs property to check if it is a frame that moves, but frames do not always have names). Therefore, after this, DocumentCompleted will be called, but not with the same Url as my WebBrowser Url property, because it was just a frame that was moved, so my program will believe that it is still moving forever.

Adding calls to Navigating and subtracting calls to DocumentCompleted will not work either. They are not always the same. I have not found a solution to this problem for several months; I use solutions 1 and 2 and hope that they will work in most cases. My plan was to use a timer if some web page has errors or something like that, but I don't think Google Maps has errors. I could still use it, but the only ugly solution would be to burn my computer.

Edit: So far this is the closest 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(); } } } 

SafeWebBrowser inherits WebBrowser . The OnNavigating , OnNavigated and OnDocumentCompleted call the corresponding WebBrowser overridden methods. The IsSafeNavigating property is the one that would let me know whether it will swim or not.

+6
c # browser webbrowser-control
source share
3 answers

No, there is no way that will work for all websites. Reason: Javascript can cause sudden navigation (I think AJAX ...), and there is no way to predict when or when this will happen. Unless you are developing for a particular site, of course.

I recommend asking another question: what happens if the navigation takes place while you want to do something? Once you find out that you can catch a mistake.

+1
source share

Waiting for a document to load is a difficult problem, but you want to constantly check for the presence of .ReadyState and .Busy (do not forget about this). I will give you some general information that you will need, and I will answer your specific question at the end.

BTW, NC = NavigateComplete and DC = DocumentComplete.

In addition, if there are frames on the page you expect, you need to get a link to it and check their .busy and .readystate, as well as if the frames are nested, the nested frames .readystate and .busy as well, so you need to write a function that recursively returns those links.

Now, no matter how many frames it has, the first event of the NC event is always the top document, and the last event of the DC event is always the top (parent) document.

So, you should check if there is the first call and pDisp Is WebBrowser1.object (literally what you enter in the if statement), then you know its NC for the top-level document, and then you expect the same object to appear in the DC event, therefore save pDisp for the global variable and wait until the DC is started and the pDisp DC is equal to the global pDisp that you saved during the first emergency event (like in pDisp that you saved globally in the first NC event that was triggered). So, once you know that pDisp has been returned to DC, you know that the whole document is complete.

This will improve your currect method, however, to make it more stupid, you also need to check the frames, since even if you have done all of the above, it is more than 90% good, but not 100% proof of a fool, more needs to be done for this.

To make successful NC / DC counting meaningful (maybe believe me), you need to store the pDisp of each NC in an array or collection if and only if it does not already exist in that array / collection. The key to doing this work is checking for duplication of NC pDisp, not adding it if it exists. Since it happens that the NC starts with a specific URL, then there is a server-side redirect or URL change, and when that happens, the NC starts again, BUT this happens with the same pDisp object that was used for the old URL . Thus, the same pDisp object is dispatched to the second NC event, which occurs a second time with the new URL, but is still executed with the same pDisp object.

Now, since you have an account of all the unique pDisp NC objects, you can (one after the other) delete them as each DC event occurs by performing the typical comparison If pDisp Is pDispArray(i) Then (this is in VB) wrapped in For Loop , and for each shot, the counter of your array will approach 0. This is the exact way to do this, but this is not enough, since another NC / DC pair may appear after your count reaches 0. Also, you should remember to do the same for checking the pDisp loop in the NavigateError event as in the DC event, because when fussing As a result of the navigation error, the NavigateError event is raised in the INSTEAD of the DC event.

I know that it was a lot, but it took me years to handle this terrible control to figure it out, I have different code and methods if you need, but some of the things I mentioned here regarding WB Navigation that were really ready were not previously published on the Internet, so I really hope you find them useful and let me know how you are going. In addition, if you want / need clarification on some of this, let me know, unfortunately, not all of the above, if you want to be 100% sure that the web page is loaded, welcome.

PS: Also, I forgot to mention, relying on the URL to do any calculations, is an inaccurate and very bad idea, since several frames can have the same URL - for example, www.microsoft.com does this, there are 3 frames or the so-called main MS site, which you see in the address bar. Do not use a URL for any counting method.

+3
source share

First I converted the document to XML, and then I used my magic method:

  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"); } 

magic method:

 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; } 

where m_bStopped is false by default, IE_PARSER_DELAY is the timeout value. Hope this helps.

0
source share

All Articles