Skip to content

Commit f94e53d

Browse files
committed
WPF - Resize Hack for when browser switches from hidden to visible (#2779)
- Add 1 to the width/height rather than subtracts like the hack in the previous versions - Delay between resizes is now configurable via ResizeHackForIssue2279DelayInMs
1 parent 807d79b commit f94e53d

File tree

1 file changed

+184
-22
lines changed

1 file changed

+184
-22
lines changed

CefSharp.Wpf/ChromiumWebBrowser.cs

Lines changed: 184 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,29 @@ public partial class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBro
138138
/// </summary>
139139
private static bool DesignMode;
140140

141+
private bool resizeHackForIssue2779Enabled;
142+
private CefSharp.Structs.Size? resizeHackForIssue2779Size;
143+
141144
/// <summary>
142145
/// The value for disposal, if it's 1 (one) then this instance is either disposed
143146
/// or in the process of getting disposed
144147
/// </summary>
145148
private int disposeSignaled;
146149

150+
/// <summary>
151+
/// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
152+
/// Enabled by default
153+
/// </summary>
154+
public bool EnableResizeHackForIssue2779 { get; set; }
155+
156+
/// <summary>
157+
/// Number of miliseconds to wait after resizing the browser when it first
158+
/// becomes visible. After the delay the browser will revert to it's
159+
/// original size.
160+
/// Hack to work around issue https://github.com/cefsharp/CefSharp/issues/2779
161+
/// </summary>
162+
public int ResizeHackForIssue2279DelayInMs { get; set; }
163+
147164
/// <summary>
148165
/// Gets a value indicating whether this instance is disposed.
149166
/// </summary>
@@ -493,6 +510,9 @@ public ChromiumWebBrowser(string initialAddress)
493510
[MethodImpl(MethodImplOptions.NoInlining)]
494511
private void NoInliningConstructor()
495512
{
513+
EnableResizeHackForIssue2779 = true;
514+
ResizeHackForIssue2279DelayInMs = 50;
515+
496516
//Initialize CEF if it hasn't already been initialized
497517
if (!Cef.IsInitialized)
498518
{
@@ -760,7 +780,18 @@ Rect IRenderWebBrowser.GetViewRect()
760780
/// <returns>View Rectangle</returns>
761781
protected virtual Rect GetViewRect()
762782
{
763-
return viewRect;
783+
//Take a local copy as the value is set on a different thread,
784+
//Its possible the struct is set to null after our initial check.
785+
var resizeRect = resizeHackForIssue2779Size;
786+
787+
if (resizeRect == null)
788+
{
789+
return viewRect;
790+
}
791+
792+
var size = resizeRect.Value;
793+
794+
return new Rect(0, 0, size.Width, size.Height);
764795
}
765796

766797
bool IRenderWebBrowser.GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY)
@@ -905,6 +936,11 @@ void IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buf
905936
/// <param name="height">height</param>
906937
protected virtual void OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height)
907938
{
939+
if (resizeHackForIssue2779Enabled)
940+
{
941+
return;
942+
}
943+
908944
var paint = Paint;
909945
if (paint != null)
910946
{
@@ -1633,7 +1669,7 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
16331669
}
16341670
}
16351671

1636-
private void OnWindowStateChanged(object sender, EventArgs e)
1672+
private async void OnWindowStateChanged(object sender, EventArgs e)
16371673
{
16381674
var window = (Window)sender;
16391675

@@ -1644,16 +1680,54 @@ private void OnWindowStateChanged(object sender, EventArgs e)
16441680
{
16451681
if (previousWindowState == WindowState.Minimized && browser != null)
16461682
{
1647-
browser.GetHost().WasHidden(false);
1683+
await CefUiThreadRunAsync(async () =>
1684+
{
1685+
var host = browser?.GetHost();
1686+
if (host != null && !host.IsDisposed)
1687+
{
1688+
try
1689+
{
1690+
host.WasHidden(false);
1691+
1692+
await ResizeHackFor2779();
1693+
}
1694+
catch (ObjectDisposedException)
1695+
{
1696+
// Because Dispose runs in another thread there's a race condition between
1697+
// that and this code running on the CEF UI thread, so the host could be disposed
1698+
// between the check and using it. We can either synchronize access using locking
1699+
// (potentially blocking the UI thread in Dispose) or catch the extremely rare
1700+
// exception, which is what we do here
1701+
}
1702+
}
1703+
});
16481704
}
1705+
16491706
break;
16501707
}
16511708
case WindowState.Minimized:
16521709
{
1653-
if (browser != null)
1710+
await CefUiThreadRunAsync(() =>
16541711
{
1655-
browser.GetHost().WasHidden(true);
1656-
}
1712+
var host = browser?.GetHost();
1713+
if (host != null && !host.IsDisposed)
1714+
{
1715+
if (EnableResizeHackForIssue2779)
1716+
{
1717+
resizeHackForIssue2779Enabled = true;
1718+
}
1719+
1720+
try
1721+
{
1722+
host.WasHidden(true);
1723+
}
1724+
catch (ObjectDisposedException)
1725+
{
1726+
// See comment in catch in OnWindowStateChanged
1727+
}
1728+
}
1729+
});
1730+
16571731
break;
16581732
}
16591733
}
@@ -1796,6 +1870,24 @@ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = Disp
17961870
}
17971871
}
17981872

1873+
protected async Task CefUiThreadRunAsync(Action action)
1874+
{
1875+
if (!IsDisposed && InternalIsBrowserInitialized())
1876+
{
1877+
if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
1878+
{
1879+
action();
1880+
}
1881+
else
1882+
{
1883+
await Cef.UIThreadTaskFactory.StartNew(delegate
1884+
{
1885+
action();
1886+
});
1887+
}
1888+
}
1889+
}
1890+
17991891
/// <summary>
18001892
/// Runs the specific Action on the Dispatcher in an sync fashion
18011893
/// </summary>
@@ -1818,52 +1910,97 @@ private void UiThreadRunSync(Action action, DispatcherPriority priority = Dispat
18181910
/// </summary>
18191911
/// <param name="sender">The sender.</param>
18201912
/// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
1821-
private void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
1913+
private async void OnActualSizeChanged(object sender, SizeChangedEventArgs e)
18221914
{
18231915
// Initialize RenderClientAdapter when WPF has calculated the actual size of current content.
18241916
CreateOffscreenBrowser(e.NewSize);
18251917

1918+
if (InternalIsBrowserInitialized())
1919+
{
1920+
await CefUiThreadRunAsync(() =>
1921+
{
1922+
SetViewRect(e);
1923+
1924+
var host = browser?.GetHost();
1925+
if (host != null && !host.IsDisposed)
1926+
{
1927+
try
1928+
{
1929+
host.WasResized();
1930+
}
1931+
catch (ObjectDisposedException)
1932+
{
1933+
// See comment in catch in OnWindowStateChanged
1934+
}
1935+
}
1936+
});
1937+
}
1938+
else
1939+
{
1940+
//If the browser hasn't been created yet then directly update the viewRect
1941+
SetViewRect(e);
1942+
}
1943+
}
1944+
1945+
private void SetViewRect(SizeChangedEventArgs e)
1946+
{
18261947
//NOTE: Previous we used Math.Ceiling to round the sizing up, we
18271948
//now set UseLayoutRounding = true; on the control so the sizes are
18281949
//already rounded to a whole number for us.
18291950
viewRect = new Rect(0, 0, (int)e.NewSize.Width, (int)e.NewSize.Height);
1830-
1831-
if (browser != null)
1832-
{
1833-
browser.GetHost().WasResized();
1834-
}
18351951
}
18361952

18371953
/// <summary>
18381954
/// Handles the <see cref="E:IsVisibleChanged" /> event.
18391955
/// </summary>
18401956
/// <param name="sender">The sender.</param>
18411957
/// <param name="args">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
1842-
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
1958+
private async void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
18431959
{
18441960
var isVisible = (bool)args.NewValue;
18451961

18461962
if (browser != null)
18471963
{
1848-
var host = browser.GetHost();
1849-
host.WasHidden(!isVisible);
1964+
await CefUiThreadRunAsync(async () =>
1965+
{
1966+
var host = browser?.GetHost();
1967+
if (host != null && !host.IsDisposed)
1968+
{
1969+
try
1970+
{
1971+
host.WasHidden(!isVisible);
1972+
1973+
if (isVisible)
1974+
{
1975+
await ResizeHackFor2779();
1976+
}
1977+
else if (EnableResizeHackForIssue2779)
1978+
{
1979+
resizeHackForIssue2779Enabled = true;
1980+
}
1981+
}
1982+
catch (ObjectDisposedException)
1983+
{
1984+
// See comment in catch in OnWindowStateChanged
1985+
}
1986+
}
1987+
});
18501988

1851-
if (isVisible)
1989+
if (browser != null)
18521990
{
18531991
//Fix for #1778 - When browser becomes visible we update the zoom level
18541992
//browsers of the same origin will share the same zoomlevel and
18551993
//we need to track the update, so our ZoomLevelProperty works
18561994
//properly
1857-
host.GetZoomLevelAsync().ContinueWith(t =>
1995+
var zoomLevel = await browser.GetHost().GetZoomLevelAsync();
1996+
1997+
UiThreadRunAsync(() =>
18581998
{
18591999
if (!IsDisposed)
18602000
{
1861-
SetCurrentValue(ZoomLevelProperty, t.Result);
2001+
SetCurrentValue(ZoomLevelProperty, zoomLevel);
18622002
}
1863-
},
1864-
CancellationToken.None,
1865-
TaskContinuationOptions.OnlyOnRanToCompletion,
1866-
TaskScheduler.FromCurrentSynchronizationContext());
2003+
});
18672004
}
18682005
}
18692006
}
@@ -2498,5 +2635,30 @@ public IBrowser GetBrowser()
24982635

24992636
return browser;
25002637
}
2638+
2639+
private async Task ResizeHackFor2779()
2640+
{
2641+
if (EnableResizeHackForIssue2779)
2642+
{
2643+
var host = browser?.GetHost();
2644+
if (host != null && !host.IsDisposed)
2645+
{
2646+
resizeHackForIssue2779Size = new Structs.Size(viewRect.Width + 1, viewRect.Height + 1);
2647+
host.WasResized();
2648+
2649+
await Task.Delay(ResizeHackForIssue2279DelayInMs);
2650+
2651+
if (!host.IsDisposed)
2652+
{
2653+
resizeHackForIssue2779Size = null;
2654+
host.WasResized();
2655+
2656+
resizeHackForIssue2779Enabled = false;
2657+
2658+
host.Invalidate(PaintElementType.View);
2659+
}
2660+
}
2661+
}
2662+
}
25012663
}
25022664
}

0 commit comments

Comments
 (0)