c# webBrowser.ReadyState will not achieve Complete state inside DocumentCompleted event? - c#

I have a problem with my little c# project.
I need to somehow navigate through a site, performing a few simple actions on each page. My solution to it was along the lines of this:
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var button = webBrowser1.Document.GetItemById("next_page_button");
button.InvokeMember("click");
webBrowser1.Refresh();
//here's my ugly solution which works
do {} while (webBrowser1.ReadyState!=WebBrowserReadyState.Complete);
webBrowser1.Navigate("http://www.webtest.com/page3");
webBrowser1.Refresh();
//same method of waiting for loading, causes endless loop this time
do {} while (webBrowser1.ReadyState!=WebBrowserReadyState.Complete);
var images = webBrowser1.Document.GetElementsByTagName("img");
//and then I do stuff with all them images..
So basically my program detects that the webBrowser loaded a page just fine the first time with that ugly while loop, but then, after the navigate() command it enters the second loop and never comes out of it. How come?
I've checked and double checked everything in debug mode, going through every step.
I could use your advice on organizing this program better for sure. xD

After two years i don't know it would help!!
but for others, the main thread is getting busy with your while loop so webbrowser object can not do anything, you need to implement this
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler((object sender, WebBrowserDocumentCompletedEventArgs arg) =>
{
/// do anything you want with webbrowser document.
})
or using Application.DoEvents(), this will make your main thread loop once and the webbrowser object load it's resources like javascripts files.

Related

Click button after webBrowser1_DocumentCompleted event

I have a C# 4.0 WinForms application, which has a WebBrowser control and 2-buttons.
Clicking the first button sends a URL to the browser to navigate to a specified webSite.
Clicking the second button parses the OuterHtml of the webBrowser1.Document, looking for an "https://..." link for File Download.
The code then uses a webClient.DownloadFileAsync to pull down a file for further use in the application.
The above code successfully works, if I manually click those buttons.
In an effort to automate this for the end-user, I place the first button's click event, i.e. btnDisplayWeb.PerformClick(); in the form's Form1_Load event. This also works, allowing the webBrowser1 to populate its Document with the desired webSite.
However, I am unable to programatically click the 2nd button to acquire the web link for file download.
I have tried to place the 2nd buttons click event within the browser's DocumentCompleted event, as shown below.
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
btnMyUrl.PerformClick();
}
However, from what I've read on StackOverFlow and other sites, it's possible that this particular event gets called more than once, and hence it fails.
I've also attempted to loop for a number of seconds, or even use a Thread.Sleep(xxxx), but the browser window fails to populate until the sleep or timer stops.
I attempted to use the suggestions found on the following StackOverFlow site shown below.
How to use WebBrowser control DocumentCompleted event in C#?
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
}
}
However, in parsing the OuterHtml, nothing is returned in the first two sections, and in the third section, other elements are returned instead of the desired "https://..." link for File Download.
Interestingly, if I use a webBrowser1.ReadyState event, as shown below, and place a MessageBox inside DocumentCompleted, this seems to allow the browser document to complete, because after clicking the OK button, the parsing is successful.
if (webBrowser1.ReadyState == WebBrowserReadyState.Complete)
{
MessageBox.Show("waiting", "CHECKING");
btnMyUrl.PerformClick();
}
However, I then have the difficulty of finding a way to click the OK button of the MessageBox.
Is there another event that occurs after the DocumentCompleted event.
OR, can someone suggest how to programmatically close the MessageBox?
If this can be done in code, then I can perform the buttonClick() of the 2nd button in that section of code.
After finding that the addition of a MessageBox allows the webBrowser1.Document to complete, and using webBrowser1.ReadyState event within the webBrowser_DocumentCompleted event, all I needed to do, was to find a way to programmatically close the MessageBox.
Further searching on StackOverFlow revealed the following solution on the site below.
Close a MessageBox after several seconds
Implementing the AutoClosingMessageBox, and setting a time interval, closed the MessageBox and allowed my button click, i.e. btnMyUrl.PerformClick(); to successfully parse the OuterHtml and now the code works properly.
Hopefully, if someone else discovers that placing a MessageBox within the webBrowser_DocumentCompleted event allows the document to complete; the aforementioned AutoClosingMessageBox will assist them as well.

How to let the webBrowser_Documentcompleted fire multiple times

I made a simple webbrowser in c#, which keeps reloading a page and then does something when the page is loaded.
However, after the first time, the following function doesn't fire anymore:
public void gotourl()
{
webBrowser.Navigate("Stackoverflow.com");
}
public void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
MessageBox.Show("Page loaded succesfully."); // only fires once
// waiting for a few seconds by using a timer
gotourl();
}
However, I did reload the page. The documentcompleted state simply doesn't fire again.
Is there a way to let the function fire every time I navigate to an url?
( I also tried webBrowser.Refresh() )
EDIT: I added the unbelievable solution..
The solution is unbelievable. After hours of searching and trying things, I found the answer.
In the properties of webBrowser1, I set the property "AllowNavigation" to false.
If set to false, it only registers the webBrowser1.DocumentCompleted() function only ONCE, but when the AllowNavigation property is set to true(which it is by default), the DocumentCompleted() function repeats.
I have no clue why it works this way and I hope people with the same problem find this answer, as it is the only answer on the net..

Gtk.Application.Invoke not working

Application.Invoke occurs inside of a foreach loop, which I removed to save space. When bwSearchEngines completes THEN the ui updates with the code inside of the Invoke. How can this be corrected? it needs to update everytime the invoke is called, which is called inside of a foreach loop that I removed.
When the button is pressed the button click event occurs, which starts the task.
lblStatus is a label on a statusbar, tvResults is a TreeView, and OnBtnSearchClicked is a button click event
To be clear, I am running Gnome 3.4.2 on Debian wheezy, NOT Windows. This is not a Windows app. I am using MonoDevelop 3.0.3.2 and Gtk# 2.12
public void bwSearchEngines()
{
//a couple foreach loops removed to save space
Application.Invoke(delegate {
lblStatus.Text = (engines.listSearchSesults.Count+1).ToString();
tvResults.Model = lsTorrents;
});
}
protected void OnBtnSearchClicked (object sender, EventArgs e)
{
lsTorrents.Clear(); //first remove data from TreeView before populating it
btnSearch.Visible = false; //when a search is started disable search button
Task.Factory.StartNew( () => {
bwSearchEngines();
}).ContinueWith(_ => btnSearch.Visible = true);
}
since no one wants to help I had to figure this out for myself. My solution is not what I wanted because it locks the ui! bwSearchEngines is called in the button click event and not in the started thread. so bwSearchEngines is called directly in the button event, which locks the ui until the function returns (function completes). this function is long running and shouldn't lock the ui BUT it does update like i wanted. Oh right after the Application.Invoke i had to add while(Application.EventsPending()) Application.RunIteration();
unfortunately this cannot work this way because bwSearchEngines will run really long, so blocking the ui is not an option but i must update the ui. what i have presented so far need to work together

How do I go to a webpage, load, then do some code?

I've tried using Sleep but that just froze the program. Is it messing up because it's in the timer? I can get it to go to the site and not do code, but once I put other code it doesn't load properly.
I also tried putting code in:
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) but then I had even more problems.
private void timer1_Tick(object sender, EventArgs e)
{
//code here
if (click == null)
{
webBrowser1.Url = new System.Uri("http://url.com", System.UriKind.Absolute);
timer1.Stop();
//load or wait x seconds
timer1.Start();
}
EDIT:
I'm trying to go from one page to the next and doing code once the page is loaded. If I put code in webBrowser1_DocumentCompleted it messes up the code before it. How do I add DocumentCompleted to only this one instance?
First, it seems like ur approach is wrong,
Open the url in the web browser control.
Wait for DocumentCompletedEvent. It will come once page is loaded fully.
Write ur code in document completed event based on your criteria/condition.

WebBrowser DocumentCompleted event fired more than once

I've been researching this stuff and everyone seems to agree that the solution is to check the ReadyState of the Web Browser until is set to Complete.
But actually the event is sometimes fired with the ReadyState set to Complete several times.
I don't think there is a solution with that crappy .NET WebBrowser, but there might be one if I use the underlying DOM component.
Only problem is, I have no idea how do access the DOM component behind the WebBrowser that fires the DocumentCompleted event.
DocumentCompleted will fire for each frame in the web page. The hard way is to count off the frames, shows you how to access the DOM:
private int mFrameCount;
private void startNavigate(string url) {
mFrameCount = 0;
webBrowser1.Navigate(url);
}
private void DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
mFrameCount += 1;
bool done = true;
if (webBrowser1.Document != null) {
HtmlWindow win = webBrowser1.Document.Window;
if (win.Frames.Count > mFrameCount && win.Frames.Count > 0) done = false;
}
if (done) {
Console.WriteLine("Now it is really done");
}
}
The easy way is to check the URL that completed loading:
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.Equals(webBrowser1.Url)) {
Console.WriteLine("Now it is really done");
}
}
This would probably happen if the page uses Javascript or <meta refresh> to redirect to another page.
If so, there's no good workaround.
I can't find anything that will give 100% certainty.
Mentioned example (e.Url.Equals(webBrowser1.Url)) may work for a simple WebBrowser.Navigate(url), however, in my case I click nodes in code to open new frames in existing frames. Mostly the number of times "Navigating" and "DocumentCompleted" fire will be the same, but again NOT always. "isBusy = false" and "ReadyState = Complete" will always be the case when it's finished (at least so far) but it will also a few times have this state when it's still loading. Counting frames also seems useless for me, in one case DocumentCompleted is fired 23 times, however, all frames and sub(-sub-sub and so on) frames are 14 in total.
The only thing that seems to work is wait a short period (1 or 2 seconds?) to see if anything happens (any events fired, any state changes).
Hmm, I found another solution for me. Often we're not interested in the whole page being loaded, often we want certain elements to exists. So after each DocumentCompleted and when "isBusy = false" and "ReadyState = Complete" we can search the DOM if this element exists.
In my experience it's impossible to tell when a web page has finished loading until DocumentCompleted hasn't fired for a while. So I refresh a timer for around 1000ms every time the DocumentCompleted event triggers. Then when the timer times out I process the web page.

Categories