Hello. It seems to me that if load the CefSharp control with a web page when the control is not visible (hidden), it doesn't actually load the web page until the web page is shown? I say this because I have a web page that loads very slowly (10 seconds to load). I've tried loading the web page the moment my app starts up from the main form's Load even, in the hope that when the user is ready to see the page with the CefSharp control the page is already fully loaded. However, despite doing this, when I make the CefSharp host page visible, I still have a long delay before the web page is fully loaded/visible.
If I'm right about this, is there any way to force the CefSharp control to fully render the web page before it is made visible? If I'm wrong about this, any ideas you might have for me to use to try and fix this would be appreciated.
I've tried loading the web page the moment my app starts up from the main form's Load even, in the hope that when the user is ready to see the page with the CefSharp control the page is already fully loaded.
I recommend you modify the WinForms example project, and set breakpoints in the CefSharp.WinForms.ChromiumWebBrowser class so you can better understand what is happening. The more technically accurate answer is that the browser is only initialized when a window handle is created for the ChromiumWebBrowser control:
If I'm right about this, is there any way to force the CefSharp control to fully render the web page before it is made visible? If I'm wrong about this, any ideas you might have for me to use to try and fix this would be appreciated.
Knowing the details from above, the real question becomes: "is there any way to force my WinForms control to have its handle created before it is visible?". Luckily Stackoverflow will probably offer better help than CefSharp Github issues for this more general question :smile:
@roschler Any follow up on this? One solution I've seen suggested in the past is to have an overlay hiding the browser and removing the overlay when the page has finished loading.
Closing due to lack of feedback.
As @jankurianski said, chrome browser is rendered when its handle is created.
I solved it by just printing browser's handle once which forces handle to be initialized.
// Create a browser component
browser = new ChromiumWebBrowser(this.url);
Console.WriteLine(browser.Handle);
Another option for wpf is to create a parent class and call CreateOffscreenBrowser (as it doesn't use the handle init). This is not perfect (even if properly passing the correct window size) but can trigger a load.
Is there any updates regarding this issue ?
Can this be done cleaner?
@C1rdec yes: https://gist.github.com/amaitland/0aa9532aa861701e9869dd1a4b46d233 @amaitland put it together and its cleanest solution I have seen:)
I had planned to add a constructor overload to the WPF version, just hadn't got around to it.
Added in https://github.com/cefsharp/CefSharp/commit/c88618e6638440f0150e0935d41872b2399ef6b2, change will be in the next major release (probably 79)
For WinForms, calling https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.createcontrol?view=netframework-4.8 is the recommended approach, writing the Handle to the console isn't necessary (and not particularly maintainable code as the intention isn't clear).
@amaitland It may not be a good idea to only offer overloading the constructor, there are several handlers etc that have to be set after the browser is created but before used. Exposing a helper function might be better.
Exposing a helper function might be better.
@mitchcapper Suggestions on a method name? Sometimes the hardest part 馃槃
I called it CreateOffscreenBrowser but as the code isn't for others didn't give a whole lot of thought into it. ForceBrowserRender?
Here is my segment that does it. I could do a PR if this is the format just wasn't sure if it was clean enough.
private readonly IntPtr parentWindowHandle;
public CustomChromiumWebBrowser(Window parent, Size size, string address = null) : base(address) {
this.parentWindowHandle = ((HwndSource)PresentationSource.FromVisual(parent)).Handle;
create_at_size = size;
Loaded += OnCustomChromiumWebBrowserLoaded;
}
private Size create_at_size = Size.Empty;
public void CreateOffscreenBrowser() {
if (create_at_size == Size.Empty)
throw new Exception("Must have had parent specified before");
this.CreateOffscreenBrowser(create_at_size);
}
protected override IWindowInfo CreateOffscreenBrowserWindowInfo(IntPtr handle) {
//If we aren't attached to the Visual Tree when the browser is created
//Then we should use the parentWindowHandle(HWND) passed in to the constructor
return base.CreateOffscreenBrowserWindowInfo(handle == IntPtr.Zero ? parentWindowHandle : handle);
}
private void OnCustomChromiumWebBrowserLoaded(object sender, System.Windows.RoutedEventArgs e) {
Loaded -= OnCustomChromiumWebBrowserLoaded;
//If the browser has finished rendering before we attach to the Visual Tree
//then ask for a new frame to be generated
var host = this.GetBrowserHost();
if (host != null) {
host.Invalidate(PaintElementType.View);
}
}
but as the code isn't for others didn't give a whole lot of thought into it. ForceBrowserRender?
I've just called it CreateBrowser for now, method added in https://github.com/cefsharp/CefSharp/commit/c8469aea9d93d03b0c94a8dfea8e5bd8333aa1ec
If anyone has another suggestion for a name please say so.
I might rename the method from CreateBrowser to InitializeBrowser as that somewhat sites better with the WPF naming, e.g. InitializeComponent
Any chance that this can also be added for CefSharp.WinForms? I could make a PR for it.
The WinForms version creates the browser when the Handle is created, you can force handler creation by calling CreateControl as referenced in https://github.com/cefsharp/CefSharp/issues/1406#issuecomment-555221087
No additional code changes are nessicary.
In my case the ChromiumWebBrowser is not initially visible. So CreateControl does nothing. But Microsoft's documentation actually says
access the Handle property to create the control's handle regardless of the control's visibility
So i guess I will have to stick with accessing the handle.
Thanks!
WinForms
In most cases the following should create a handle and load the control as expected.
c#
var b1 = new ChromiumWebBrowser("www.google.com.au");
//CreateControl creates a handle, then the underlying CEF Browser is created in an async fashion
b1.CreateControl();
In the rare case this doesn't work then you can call Control.CreateHandle() instead, though this is generally discouraged.
You typically should not call the CreateHandle method directly. The preferred method is to call the CreateControl method, which forces a handle to be created for the control and its child controls when the control is created.
As per Control.CreateHandle. In our case there are no child controls so this should be fine in most cases.
Accessing the Control.Handle calls CreateHandle, see https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,2863
Control.CreateHandle and Control.CreateControl are standard .Net Functions.
Most helpful comment
As @jankurianski said, chrome browser is rendered when its handle is created.
I solved it by just printing browser's handle once which forces handle to be initialized.