C# WebBrowser different html document after navigate - c#

I have a really strange problem in C#:
First I use the WebBrowser control and the navigate method to browse.
wb_email.Navigate("https://registrierung.web.de");
Now I can change the innerText of htmlelements without any problems.
wb_email.Document.GetElementById("id4").InnerText = "Alexander";
But when I reload the page by simply using the navigate method with the same url again,
I get a null exception. It seems as he can't find the element.
So I used an inspector for Firefox to see if the htmlelement really changed, after reloading.
But only the url is changing, htmlelements are all the same.
What I'm doing wrong?

You're just changing the DOM in the displayed page. When you reload the page, the WebBrowser instance will just refresh the DOM from the server again and lose your changes.
The WebBrowser class isn't designed for editing rendered pages inside itself, as it's basically just a wrapper to an embedded Internet Explorer instance.

Make sure the website has finished loading before accessing any element. Like:
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser_DocumentCompleted);
void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Access elements here
}

Related

How to properly display the current and real url loaded in a WebBrowser control?

In my Form, I added a WebBrowser and a TextBox on which I would like to display the current loaded Url.
Note what the Microsoft Docs says:
WebBrowser.Navigating event:
Occurs before the WebBrowser control navigates to a new document
WebBrowser.Navigated event:
Occurs when the WebBrowser control has navigated to a new document and has begun loading it.
...
Handle the DocumentCompleted event to receive notification when the WebBrowser control finishes loading the new document.
WebBrowser.DocumentCompleted event:
Occurs when the WebBrowser control finishes loading a document
...
Handle the DocumentCompleted event to receive notification when the new document finishes loading. When the DocumentCompleted event occurs, the new document is fully loaded
The order at which the events are fired is: Navigating, Navigated and DocumentCompleted, so I'm handling those events to try properly update the current url:
private void WebBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
this.TextBox1.Text = e.Url.ToString();
}
private void WebBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e) {
this.TextBox1.Text = e.Url.ToString();
}
private void WebBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
this.TextBox1.Text = e.Url.ToString();
}
The problem is that for some reason the url does not seem to properly update for some websites...
For example when navigating through Google's search engine, if I do click on the Google Images button, the url updates to "http://www.google.com/blank.html". Also, the urls that I get to display in my TextBox are not the same exact urls as I can see in Firefox or Chrome's address bar; for some reason my obtained urls have additional parameters in the query.
See it by yourself:
https://i.imgur.com/PQlSu47.gif
Is there any workaround to improve this annoying behavior so I can display the current url with efficiency as Firefox or Chrome does?. I mean, for example Firefox and Chrome will not show "http://www.google.com/blank.html" in the addres bar, neither will not show the url queries with additional parameters as I got to display (which you can see in the GIF image above).
Please note that the problem with Google website is just as an example. I'm asking for a universal solution due this issue occurs with many more websites.
Also note that if instead the WebBrowser component I use CefSharp's chromium based web browser, adapting my code to reproduce the same as I was doing to display/update the current url, then the problem is partially gone...
Using CefSharp does not shows "http://www.google.com/blank.html" when navigating through Google Images, however the query of the urls still contain additional parameters / many differences in comparison from the urls displayed in Firefox or Chrome browsers.
And apart from that, I would like to avoid using CefSharp just for solving this kind of issue...
TL;DR
Instead of URL in the event args, use URL from the web browser control:
string url = webBrowser1.Url.ToString();
The long answer
What you are missing here is that the HTML page can contain iframe elements. An iframe encapsulates HTML Window and the contained HTML Document, and it performs its own navigation. The navigation events of the WebBrowser control fire for both the top window (for which you want to display the URL) and for the iframe's. You will have to distinguish between the two.
Specifically, in your case http://www.google.com/blank.html comes from an iframe:
<html> <!-- top window -->
<body> <!-- top window's document -->
<iframe src="http://www.google.com/blank.html">
<!--
here the browser will load and "insert" HTML of blank.html
lines below don't exist in the original HTML
they are loaded and "inserted" here by the browser
-->
<html> <!-- iframe's window -->
<body> <!-- iframe's window's document -->
<!-- the body can contain additional iframes... -->
</body>
</html>
</iframe>
</body>
</html>
In general, the DOM of an HTML page is a tree of HTML Window objects, with the root window returned by the window.top property. Depending on how the page is designed, iframe's can be visible or hidden; they can be rendered in HTML by server, and also be manipulated, created, or deleted dynamically in the browser through JavaScript:
when a new iframe is created, it performs navigation to URL specified in its src attribute. If neither src nor embedded contents are specified, the navigation URL will be about:blank.
when src attribute of an existing iframe is modified, the iframe will perform navigation to the new src.
when window.location is modified of either top or iframe HTML Window, it will perform navigation to the new location.
However, determining which HTML Window (top or iframe) performs the navigation doesn't seem to be a trivial task, so a simpler approach would be just getting URL of the top window:
string url = webBrowser1.Url.ToString();
or after the DocumentCompleted event:
HtmlWindow topWindow = webBrowser1.Document.Window;
string url = topWindow.Url.ToString();
The DocumentCompleted can be fired multiple times for a given URL, because a page can contain iframes that also trigger the event. So I suggest that you update the textbox at Navigating and Navigated events only.

Unable to access a generated DOM from a WPF WebBrowser

I need to host a web page inside a WPF application, so I added a WPF WebBrowser control as follows...
<WebBrowser Name="WebBrowser" />
In the C#, I did the following...
WebBrowser.Navigate("http://tableau.visionrt.com/#/signin");
WebBrowser.LoadCompleted += PageLoadedLogin;
...and had the following method, in which I wanted to fill in the two inputs and submit the form...
private void PageLoadedLogin(object sender, NavigationEventArgs e) {
IHTMLDocument3 doc = (IHTMLDocument3)WebBrowser.Document;
HTMLInputElement u = (HTMLInputElement)doc.getElementById("username");
HTMLInputElement p = (HTMLInputElement)doc.getElementById("password");
}
However, both u and p are null, meaning I can't access the elements. I can see the elements if I view the source in a a browser, but I can't access them in code.
I tried the following, just in case I'd got the names wrong...
IHTMLElementCollection inputs = doc.getElementsByTagName("input");
...but that didn't find anything.
Now, looking at the actual HTML that the URL returns, it looks like the <input> and <button> elements are generated in JavaScript, in which case they may not have been created when the LoadCompleted event fires.
So, I tried adding a call to Thread.Sleep in a quick-and-dirty way of making sure the JavaScript had had time to create the elements, but it didn't make any difference.
I tried using the HtmlAgilityPack as suggested in this post, but that gave the same lack of results as above.
Anyone any ideas why I can't access generated elements? All of this works fine on a page with static HTML.

WebBrowser Control Retrieving jQuery Text

I am trying to retrieve whenever the website displays the following message from a jQuery event. Initially this HTML inst displayed in the HTML.
<div id="toast-container" class="toast-top-right"><div class="toast toast-error" aria-live="assertive" style="display: block;"><div class="toast-message">Check email & password.</div></div></div>
My assumption is, that the webBrowser1.DocumentText.Contains is only looking from the initial load of the content.
So I thought maybe some sort of timer would work every 5 seconds, looking to see if the code has changed - but I don't even think this is right as it's checking the code that's already loaded repeatedly?
private void timer2_Tick(object sender, EventArgs e)
{
// Checks for any errors on sign in page
if (webBrowser1.DocumentText.Contains("toast toast-error"))
{
// Toast Notifications
var signinErrorNotification = new Notification("Error", "Please check your email and password are correct.", 50, FormAnimator.AnimationMethod.Fade, FormAnimator.AnimationDirection.Left);
signinErrorNotification.Show();
}
}
How do I go about getting the latest code that's been affected by any jQuery.
P.S. My c# level is beginner.
The Document property should give you what you need.
Notice that the docs for DocumentText say
Gets or sets the HTML contents of the page displayed in the WebBrowser
control.
For Document they say
Gets an HtmlDocument representing the Web page currently displayed in the WebBrowser control.
To me that's saying that DocumentText is like the starting document and Document is the current DOM. Also see https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-access-the-managed-html-document-object-model

Stop multiple WebBrowsers from starting yt-videos?

I'm working with a winform TabControl showing WebBrowsercontrols to display youtube videos.
However with two videos or more it becomes really annoying as all videos start directly.
I basically need to find out if there is a JS function, html code or a simple WebBrowser property to change, so videos are paused.
It might come in handy to find something like that for video quality too.
Has anybody ever heard of/seen where this option is stored? Or maybe the Js function itself being invoked when manually setting the quality?
EDIT:
b.DocumentCompleted += delegate { b.DocumentText=b.DocumentText.Insert(b.DocumentText.IndexOf("class=\"video-stream html5-main-video\""), "autoplay=false ");};
b.Url = new System.Uri(inp[s], System.UriKind.Absolute);
Basically this should add a new Event handler on each webbrowser form that modifies the DocumentText when the Uri that is called during creation has loaded.
Even though the browser debugger shows
<video tabindex="-1" class="video-stream html5-main-video" controlslist="nodownload" style=... src=...></video>
this isn't in the actual source code.
However I found
$oa=function(a){g.S(a.o,"video-stream");g.S(a.o,"html5-main-video");var b=a.app.g;b.zc&&a.o.setAttribute("data-no-fullscreen",!0);b.Oh&&(a.o.setAttribute("webkit-playsinline",""),a.o.setAttribute("playsinline",""));b.Nr&&a.o&&a.P(a.o,"click",a.o.play,a.o)};
in the base.js. Is it possible that youtube generates the html from the js?
How can I modify the video-tag attributes then?
I tried to modify when the event handler manipulates the video tag, since there may be DocumentCompleted events thrown from scripts or anything.
delegate (object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e){
if (e.Url.AbsolutePath != ((System.Windows.Forms.WebBrowser)sender).Url.AbsolutePath){
//...
However it still fails as there is no occurance of the specific class that on the video tag.
I now dodged this by loading the Url only when the browser tab is selected, if someone finds a real solution, feel free to share

WebBrowser control must reload content every time app page is revisited (Windows Phone 7, Silverlight)

I'm writing an app for Windows Phone 7/Silverlight. When the app is either tombstoned and reactivated while on the app page containing the WebBrowser control (I've saved the Uri in app state) or that same app page is navigated to by NavigationService.GoBack() or the phone back button, it seems that as long as the control still has the webbrowser.source value, it should then render just fine, but this is not the case. Unless I use the Navigate() method, it shows a white/blank screen, no matter what I try. Unfortunately, using the Navigate() method causes the web content to download again, unnecessarily. It's especially frustrating when only a GoBack() is used to get back to the application page with the WebBrowser control, which is quite frequent in my app.
private void OnWebBrowserLoaded(object sender, RoutedEventArgs e)
{
//webBrowser1.Source = CurrentUri; //does not work, results in white/blank browser page
webBrowser1.Navigate(CurrentUri); //works, but page has to reload from web, bad UX
}
Any suggestions on a way around this problem? I've also tried putting this same code in the page loaded handler. It behaves in the same poor manner.
I've also tried saving off the HTML (SaveToString) and reloading it from app state (NavigateToString), but the web page does not render completely for some reason, even though the HTML appears fine. Also, I'd like to have access to the Host and Uri properties. I could probably work around that, if I could get the HTML to render OK from NavigateToString.
Thanks,
Jay
You should use browsertask:
using Microsoft.Phone.Tasks;
WebBrowserTask browse = new WebBrowserTask();
browse.Uri = new Uri(URL, UriKind.RelativeOrAbsolute);
//new Uri(URL,UriKind.RelativeOrAbsolute);
browse.Show();
This should solve your issue.
URL will be the URL of the page you want to visit.

Categories