WPF - Browser not correctly refreshing on Resize >= 127.3.50
Is there an existing issue for this?
- [X] I have searched both open/closed issues, no issue already exists.
CefSharp Version
129.0.110
Operating System
Windows 11
Architecture
x64
.Net Version
.NET Framework 4.8
Implementation
WPF
Reproduction Steps
Create a WPF Window with a Grid, add several ChromiumWebBrowsers into each grid. Load some simple HTML content into each browser. Start App and resize the window -> WebBrowser is not resized according to the window / grid size changes -> When using the mouse scroll wheel, the browser size is updated correctly. This behaviour is observed first in 127.3.50. In 126.2.180.0, everything was ok
The following project contains some sample code, as well as a video: See: https://github.com/oetjen/ChromiumResizeProblem
Expected behavior
As in 126.2.180.0 -> Browser windows should resize
Actual behavior
Browser windows do not resize
Regression?
Worked in 126.2.180.0
Known Workarounds
none
Does this problem also occur in the CEF Sample Application
No
Other information
No response
I also see wpf ChromiumWebBrowser resizing issues in my demo app for Baksteen.Blazor.CefSharp, .net 8.0. When enlarging the window the webview seems to automatically resize up to a certain size, but then it stops, and it will only suddenly resize when I start hovering the mouse over elements in the webview. I've tested this issue occurs both in 128.4.90 and 129.0.110.
Screenrecording: https://github.com/user-attachments/assets/ad6ec647-095e-48ff-badf-ee96e41cf2e7
There's been some major upstream changes recently. See https://github.com/cefsharp/CefSharp/issues/4795 for more details.
There's a pretty high change this is an upsteram issue. There's only been minor changes to the WPF implementation over the last few versions.
Make sure you have an app.manifest with Win 10 compatibility set so GPU detection works correctly.
https://github.com/cefsharp/CefSharp/wiki/Quick-Start-For-MS-.Net-4.x#40-add-appmanifest-to-your-application
Resize Problem in WPF Grid since 127.3.50
Using version 127.3.50 can you please try setting the following option, in your App.xaml.cs is ane asy place.
c#
var settings = new CefSettings();
settings .ChromeRuntime = false;
settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyCefSharpApp\\Cache")
var initialized = Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
Create a WPF Window with a Grid, add several ChromiumWebBrowsers into each grid.
Is a grid actually relevant? Or is it just multiple browsers displayed at the same time?
When enlarging the window the webview seems to automatically resize up to a certain size, but then it stops, and it will only suddenly resize when I start hovering the mouse over elements in the webview. I've tested this issue occurs both in 128.4.90 and 129.0.110.
Based on this I don't think multiple browsers has anything to do with the issue.
It would be helpful if someone can run a git bisect on the 127 branch and see which commit introduced the change in behavior.
@amaitland resize issue started in d3953b87ac261b45f0e279ae4520e765fe032477
FWIW, it seems that after this commit the call to browser.GetHost().WasResized(); in OnActualSizeChanged() no longer reliably results in the callback leading to IRenderWebBrowser.GetViewRect(). (tested using the example CefSharp.Wpf.Example.netcore)
Update: strangely enough, I cannot reproduce the resize problem in CefSharp.WinForms.Example.netcore 😕
resize issue started in d3953b8
Thanks for confirming. Have to see what changes were made upstream.
Did some testing, I can reproduce part of the problem with cefclient, have opened https://github.com/chromiumembedded/cef/issues/3822
It'll still be worth debugging cefclient to see what's difference.
Likely related https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=20038#p56379
Other rendering issue with CEF OSR https://github.com/chromiumembedded/cef/issues/3826
any update on when this issue might be fixed?
@amipatel08 fyi the wpf.hwndhost control does not have the resize issue.
Other rendering issue with
CEF OSRchromiumembedded/cef#3826
This issue needs to be fixed in CEF, you can subscribe there for updates.
You can use CefSharp.Wpf.HwndHost.ChromiumWebBrowser as an alternative (air space issues apply).
https://github.com/chromiumembedded/cef/issues/3826#issuecomment-2685865905 suggests that explicitly calling Invalidate will get the browser to start redrawing again. Example would look something like:
// Gets a warpper around the CefBrowserHost instance
// You can perform a lot of low level browser operations using this interface
var cefbrowserHost = browser.GetBrowserHost();
//You can call Invalidate to redraw/refresh the image
cefbrowserHost.Invalidate(PaintElementType.View);
I haven't tried this yet.
I generally have what I would describe as glitchy resizing when running the Minimal WPF Example and I assume it is the same issue. Sometimes when resizing the window, the content stops re-rendering until I hover over an interactable element in the page.
I tried adding the following to my main window and it seems to mostly fix the issue with the page not re-rendering at all, though the resizing is still very far from smooth.
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
Browser.GetBrowserHost()?.Invalidate(PaintElementType.View);
}
@amaitland, @MortenChristiansen thanks! I've found it slightly more reliable (but obviously not pretty) by inserting a small delay.
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
using var bridge = AsyncBridge.AsyncHelper.Wait; // https://github.com/tejacques/AsyncBridge
bridge.Run(paintWithNewSize(browserHost));
async static Task paintWithNewSize(IBrowserHost browserHost)
{
await Task.Delay(100);
browserHost.Invalidate(PaintElementType.View);
}
}
(However, this still doesn't work 100% of the time, perhaps because it still fires too early. So I ultimately went with downgrading for now.)
@MortenChristiansen @chucker Thanks for sharing! Perhaps we can add a check in to validate the size of the bitmap is the expected size and ask to generate a new frame if it's not.
I refined my redrawing logic a bit more - it might be overkill, but here it is. The motivation for this was to avoid having a large amount of redraws when you resize the window. My application uses multiple individual browsers to render different parts of the window, so this increases the potential number of redraws.
As @chucker observed, a 100ms delay seems ideal, but in an attempt to smooth it out, I schedule 3 redraws with a 50ms delay between them. I'm not sure how much of a difference it is, but I think it makes it a bit smoother. You can always experiment with adding even finer grained updates than 50ms intervals.
The use of ConcurrentDictionary<BaseBrowser, byte> is just for lack of a ConcurrentSet<T> in .net.
I wrote this code before seeing the suggestion by @amaitland - maybe such a check would make my attempted optimization pointless, though the idea of trying to rerender a number of times in succession is still relevant regardless.
public abstract class BaseBrowser : ChromiumWebBrowser
{
private static readonly ConcurrentDictionary<BaseBrowser, byte> _browsersScheduledForRedraw = [];
static BaseBrowser()
{
Task.Run(async () =>
{
while (true)
{
var browsers = _browsersScheduledForRedraw.ToArray();
_browsersScheduledForRedraw.Clear();
foreach (var browser in browsers)
browser.Key.Redraw();
await Task.Delay(50);
foreach (var browser in browsers)
browser.Key.Redraw();
await Task.Delay(50);
foreach (var browser in browsers)
browser.Key.Redraw();
}
});
}
protected BaseBrowser()
{
SizeChanged += (sender, e) => _browsersScheduledForRedraw.AddOrUpdate(this, 0, (_, _) => 0);
}
private void Redraw()
{
this.GetBrowserHost()?.Invalidate(PaintElementType.View);
}
}
Would ConcurrentBag work here? Seems you don’t need uniqueness or key access, just iteration.
The uniqueness aspect ensures there are no redundant redraws, so I think it is better to use the dictionary.
Got a lot of funny hacks in this thread. The correct way is to set rendering for best performance.
var BaseSettings = new CefSettings();
BaseSettings.SetOffScreenRenderingBestPerformanceArgs(); //Fixes window resizing issue
Cef.Initialize(BaseSettings);
//Window components need to initialize after our settings
InitializeComponent();
@KyBamboo Thanks for sharing.
Got a lot of funny hacks in this thread. The correct way is to set rendering for best performance.
Interesting this works. Not sure that I'd call it correct. This will disable GPU acceleration and GPU compositing.
Give the issue is with redrawing on resize, it might be worth trying just disabling GPU compositing.
cefSettings.CefCommandLineArgs.Add("disable-gpu-compositing");
Which might explain this:
Update: strangely enough, I cannot reproduce the resize problem in CefSharp.WinForms.Example.netcore
WinForms (GDI+) doesn't have GPU acceleration, so perhaps CefSharp.WinForms is always non-accelerated, hence not triggering this bug.
WinForms (GDI+) doesn't have GPU acceleration, so perhaps
CefSharp.WinFormsis always non-accelerated, hence not triggering this bug.
The WinForms version does support GPU Acceleration at the Chromium level. It's rendered differently, it's a native Win32 control. The equivalent is CefSharp.Wpf.HwndHost which is a native Win32 implementation using HwndHost.
WPF uses OSR rendering which is a CEF specific feature. Read more at https://github.com/cefsharp/CefSharp/wiki/General-Usage#offscreen-rendering-osr
Give the issue is with redrawing on resize, it might be worth trying just disabling GPU compositing.
cefSettings.CefCommandLineArgs.Add("disable-gpu-compositing");
This appears to resolve the rendering issues, so we'll disable by default until the upstream issue is resolved.
This appears to resolve the rendering issues, so we'll disable by default until the upstream issue is resolved.
GPU Compositing will be disabled by default in the WPF implementation until the upstream issue is resolved. Starting in M139.