Display WinForm when WebBrowser control finished loading - c#

I have WinForm with WebBrowser control inside.
I am facing an annoying flicker from WebBrowser control. This is manifested that the body of WebBrowser control (or background) is shown for about 200ms before the web page is loaded and I could not get rid of it.
I want, when call myForm.Show() to see the window when WebBrowser completely finished loading and not to see this flickering.
Is there some way when calling myForm.Show() to display the WebForm page at the moment when WebBrowser control finished loading the page completely?
I know about DocumentCompleted event and read and tried some tricks mentioned here but still can not get rid of this filled blank background before web page is shown.
Any hints?

public class MyForm : Form
{
WebBrowser _Webbrowser = new WebBrowser();
TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
public MyForm(string url)
{
_Webbrowser.ScriptErrorsSuppressed = true;
this.Controls.Add(_Webbrowser);
_Webbrowser.DocumentCompleted += (s, e) => { _tcs.TrySetResult(null); };
_Webbrowser.Navigate(url);
}
public async new void Show()
{
await _tcs.Task;
base.Show();
}
}

Related

CefSharp documentcompleted

I am trying to use cefshar browser in C# winforms and need to know how I know when page completely loaded and how I can get browser document and get html elements,
I just Initialize the browser and don't know what I should do next:
public Form1()
{
InitializeComponent();
Cef.Initialize(new CefSettings());
browser = new ChromiumWebBrowser("http://google.com");
BrowserContainer.Controls.Add(browser);
browser.Dock = DockStyle.Fill;
}
CefSharp has a LoadingStateChanged event with LoadingStateChangedArgs.
LoadingStateChangedArgs has a property called IsLoading which indicates if the page is still loading.
You should be able to subscribe to it like this:
browser.LoadingStateChanged += OnLoadingStateChanged;
The method would look like this:
private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (!args.IsLoading)
{
// Page has finished loading, do whatever you want here
}
}
I believe you can get the page source like this:
string HTML = await browser.GetSourceAsync();
You'd probably need to get to grips with something like HtmlAgility to parse it, I'm not going to cover that as it's off topic.
I ended up using:
using CefSharp;
wbAuthorization.AddressChanged += OnAddressChanged;
and
private void OnAddressChanged(
object s,
AddressChangedEventArgs e)
{
if (e.Address.StartsWith(EndUri))
{
ResultUri = new Uri(e.Address);
this.DialogResult = DialogResult.OK;
}
}
EndUri is the final page I want to examine and ResultUri contains a string I want to extract later. Just some example code from a larger class.

WPF WebBrowser scroll on reload

I have a System.Windows.Controls.WebBrowser. It has some html that is coming from another document that the user is editing. When the html changes, what I want to do is update the WebBrowser's html, then scroll the WebBrowser back to wherever it was. I am successfully cacheing the scroll offset (see How to retrieve the scrollbar position of the webbrowser control in .NET). But I can't get a callback when the load is complete. Here is what I have tried:
// constructor
public HTMLReferenceEditor()
{
InitializeComponent();
WebBrowser browser = this.EditorBrowser;
browser.LoadCompleted += Browser_LoadCompleted;
//browser.Loaded += Browser_Loaded; // commented out as it doesn't fire when the html changes . . .
}
private void Browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
CommonDebug.LogLine("LoadCompleted");
this.ScrollWebBrowser();
}
private void ScrollWebBrowser()
{
WebBrowser browser = this.EditorBrowser;
ReferenceHierarchicalViewModel rhvm = this.GetReferenceHierarchichalViewModel();
int? y = rhvm.LastKnownScrollTop; // this is the cached offset.
browser?.ScrollToY(y);
}
The LoadCompleted callbacks are firing all right. But the scrolling is not happening. I suspect the callbacks are coming too soon. But it is also possible that my scroll method is wrong:
public static void ScrollToY(this WebBrowser browser, int? yQ)
{
if (yQ.HasValue)
{
object doc = browser?.Document;
HTMLDocument castDoc = doc as HTMLDocument;
IHTMLWindow2 window = castDoc?.parentWindow;
int y = yQ.Value;
window?.scrollTo(0, y);
CommonDebug.LogLine("scrolling", window, y);
// above is custom log method; prints out something like "scrolling HTMLWindow2Class3 54", which
// at least proves that nothing is null.
}
}
How can I get the browser to scroll? Incidentally, I don't see some of the callback methods others have mentioned, e.g. DocumentCompleted mentioned here does not exist for me. Detect WebBrowser complete page loading. In other words, for some reason I don't understand, my WebBrowser is different from theirs. For me, the methods don't exist.

Load html with WebBrowser Control while Blocking UI Thread

I have a .Net 4.5 WinForms application that contains html stored in a database. The user is presented with the list of pages. As they click on a row the page is loaded into the WebBrowser. I want the process of loading the page to be synchronous with the main UI thread (IOW, I don't want the user to be able to do anything else until the page is loaded). The code for loading the html is:
public class DocLoader
{
private readonly WebBrowser browser;
readonly TaskCompletionSource<object> loadedTCS = new TaskCompletionSource<object>();
public DocLoader(WebBrowser browser)
{
this.browser = browser;
}
private void LoadedComplete(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Debug.WriteLine("doc completed");
loadedTCS.TrySetResult(null);
browser.DocumentCompleted -= LoadedComplete;
}
public Task LoadDoc(string html)
{
browser.DocumentCompleted += LoadedComplete;
browser.DocumentText = html;
return loadedTCS.Task;
}
}
Calling the code like this:
await new DocLoader(webBrowser1).LoadDoc(html);
Will result in the message loop being able to sill process messages. This is undesirable. For example the user could click on another document in the list before the previous has completed loading.
Calling the code like this:
new DocLoader(webBrowser1).LoadDoc(html).Wait();
Results in the UI freezing and the document never loading, presumably because the DocumentCompleted event will not fire w/o the message loop running.
Is there anyway to accomplish this task and keep the process synchronous with the UI Thread?
The WebBrowser requires its parent thread to pump message to operate properly. In your case, its parent thread is the main UI thread, so you cannot block it with Wait. One hack might be to disable the whole UI, as browser.DocumentText asynchronous loading is reasonably fast:
Cursor.Current = Cursors.WaitCursor;
mainFrom.Enabled = false;
try
{
await new DocLoader(webBrowser1).LoadDoc(html);
}
finally
{
mainFrom.Enabled = true;
Cursor.Current = Cursors.Default;
}
This however is quite user-unfriendly. A proper approach would be to support cancellation in your WebBrowser asynchronous loading scenario. You'd need to cancel the pending operation if the user clicks another row (while the the previous row update is still pending), and start a new one.

How should i properly invoke a WebBrowser using multiplethreads?

Problem Scope:
I'm writing an aplication to save the HTML's retrieved from the Bing and Google searches. I know there are classes to execute the Web Requests using stream such as this example, but since Google and Bing both use Javascript and Ajax to render the results into the HTML, there's no way i can simply read the stream and use get to the result i need.
The solution to this, is to use the WebBrowser class and navigate to the url i want, so that the Browser itself will handle all the Javascript and Ajax scripting executions.
MultiThreading:
In order to make it more efficient, i have the same Form aplication firing a thread for each service (one for Bing, and one for Google).
Problem:
Since i need the WebBrowser, i have instantiated one for each thread (which are 2, at this moment). According to Microsoft, there is a known bug that prevents the DocumentCompleted event from firing if the WebBrowser is not visible and is not added to a visible form aswell (for more information, follow this link).
Real Problem:
The main issue is that, the DocumentCompleted event of the browser, never fires. Never.
I have wrote a proper handler for the DocumentCompleted event that never gets the callback. For handling the wait needed for the Browser event to fire, i have implemented a AutoResetEvent with a high timeout (5 minutes), that will dispose the webbrowser thread if it does not fire the event i need after 5 minutes.
At the moment, i have the Browser created and added into a WindowsForm, both are visible, and the event is still not firing.
Some Code:
// Creating Browser Instance
browser = new WebBrowser ();
// Setting up Custom Handler to "Document Completed" Event
browser.DocumentCompleted += DocumentCompletedEvent;
// Setting Up Random Form
genericForm = new Form();
genericForm.Width = 200;
genericForm.Height = 200;
genericForm.Controls.Add (browser);
browser.Visible = true;
As for the Navigation i have the Following (method for the browser) :
public void NavigateTo (string url)
{
CompletedNavigation = false;
if (browser.ReadyState == WebBrowserReadyState.Loading) return;
genericForm.Show (); // Shows the form so that it is visible at the time the browser navigates
browser.Navigate (url);
}
And, for the call of the Navigation i have this :
// Loading URL
browser.NavigateTo(URL);
// Waiting for Our Event To Fire
if (_event.WaitOne (_timeout))
{
// Success
}
{ // Error / Timeout From the AutoResetEvent }
TL:DR:
My WebBrowser is instantiated into a another STAThread, added to a form, both are visible and shown when the Browser Navigation fires, but the DocumentCompleted event from the Browser is never fired, so the AutoResetEvent always times out and i have no response from the browser.
Thanks in Advance and sorry for the long post
Although this seems a strange way, here is my attempt.
var tasks = new Task<string>[]
{
new MyDownloader().Download("http://www.stackoverflow.com"),
new MyDownloader().Download("http://www.google.com")
};
Task.WaitAll(tasks);
Console.WriteLine(tasks[0].Result);
Console.WriteLine(tasks[1].Result);
public class MyDownloader
{
WebBrowser _wb;
TaskCompletionSource<string> _tcs;
ApplicationContext _ctx;
public Task<string> Download(string url)
{
_tcs = new TaskCompletionSource<string>();
var t = new Thread(()=>
{
_wb = new WebBrowser();
_wb.ScriptErrorsSuppressed = true;
_wb.DocumentCompleted += _wb_DocumentCompleted;
_wb.Navigate(url);
_ctx = new ApplicationContext();
Application.Run(_ctx);
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
return _tcs.Task;
}
void _wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//_tcs.TrySetResult(_wb.DocumentText);
_tcs.TrySetResult(_wb.DocumentTitle);
_ctx.ExitThread();
}
}

Webkit.Net wait for page to be fully loaded

I'm trying to make an C# windows form application, with an webbrowser.
I'm using the webkit browser: Link to the browser
The webbrowser did i put in an class file, so i can acces it through all the forms i'm going to use.
The code that's generate the webbrowser:
public static WebKit.WebKitBrowser mainBrowser = new WebKitBrowser();
I'm having this piece of code that give's some problems:
globalVars.mainBrowser.Navigate("http://www.somesite.com/");
while (globalVars.mainBrowser.IsBusy)
{
System.Threading.Thread.Sleep(500);
}
globalVars.mainBrowser.Document.GetElementById("user").TextContent = "User Name";
But it's not working. If i do an message box after the while, it shows up before it's possible to render the page...
So what is the best way to wait until the site is fully loaded?
UPDATE 1
In an standalone class file, am i making the webkit controll like this:
public static WebKit.WebKitBrowser mainBrowser = new WebKitBrowser();
And in an form, i've got now this code (thanks to Tearsdontfalls):
public void loginthen()
{
globalVars.mainBrowser.DocumentCompleted += mainBrowser_DocumentCompleted;
globalVars.mainBrowser.Navigate("http://www.somesite.com/");
}
void mainBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var send = sender as WebKit.WebKitBrowser;
if (send.Url == e.Url)
{
MessageBox.Show("Inloggen");
globalVars.mainBrowser.Document.GetElementById("user").TextContent = "User Name";
}
}
But no messagebox shows up. But if i use an local (on the same form) webkit browser, i'm getting te MessageBox. But then the user field isn't filled in.
Even an breakpoint in the documentCompleted event, isn't triggerd. So it looks like the event listner isn't working...
So why is it not working?
You can simply create an event listener on the Document Completed Event on your Webbrowser, or you can create it dynamically like that:
globalVars.mainbrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(mainbrowser_DocumentCompleted);
Where mainbrowser_DocumentCompleted is the name of the void where you can do sth like this(I used the names of your provided code):
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var send = sender as WebKit.WebKitBrowser;
if (send.Url == e.Url) {
globalVars.mainBrowser.Document.GetElementById("user").TextContent = "User Name";
}
}
Adding the following piece of code let the events fire when the browser is in invisible mode.
using (Bitmap bmp = new Bitmap(webKitBrowser.Width, webKitBrowser.Height))
{
webKitBrowser.DrawToBitmap(
bmp,
new Rectangle(
webKitBrowser.Location.X,
webKitBrowser.Location.Y,
webKitBrowser.Width,
webKitBrowser.Height
)
);
}

Categories