@@ -16,25 +16,54 @@ public partial class PowerShellControl : UserControlBase, IDragablzTabItem, IEmb
1616{
1717 #region Events
1818
19+ private bool _isDpiChanging ;
20+
1921 private void WindowGrid_SizeChanged ( object sender , SizeChangedEventArgs e )
2022 {
2123 ResizeEmbeddedWindow ( ) ;
2224 }
2325
24- private void WindowsFormsHost_DpiChanged ( object sender , DpiChangedEventArgs e )
26+ private async void WindowsFormsHost_DpiChanged ( object sender , DpiChangedEventArgs e )
2527 {
26- // Resize first so the embedded window is physically on the new monitor,
27- // then post WM_DPICHANGED with the explicit new DPI in wParam so the
28- // embedded process rescales its fonts without relying on a cross-process
29- // GetDpiForWindow() call (which is unreliable after SetParent across processes).
30- ResizeEmbeddedWindow ( ) ;
28+ if ( ! IsConnected || _appWin == IntPtr . Zero || _isDpiChanging )
29+ return ;
3130
32- if ( IsConnected && _appWin != IntPtr . Zero )
31+ _isDpiChanging = true ;
32+
33+ try
34+ {
35+ // Windows does not forward DPI change notifications across process boundaries
36+ // after SetParent. Workaround: temporarily detach the embedded window back to
37+ // a top-level window on the new monitor. Windows then delivers WM_DPICHANGED
38+ // natively to the process's own message loop so it can rescale its fonts,
39+ // exactly as it would when running standalone. Then re-embed and resize.
40+ var screen = System . Windows . Forms . Screen . FromHandle ( WindowHost . Handle ) ;
41+
42+ NativeMethods . ShowWindow ( _appWin , NativeMethods . WindowShowStyle . Hide ) ;
43+ NativeMethods . SetParent ( _appWin , IntPtr . Zero ) ;
44+
45+ // Place the window on the new monitor so Windows sends it WM_DPICHANGED.
46+ // The size (800x600) is a reasonable placeholder; the final dimensions are
47+ // set by ResizeEmbeddedWindow() after re-embedding.
48+ NativeMethods . SetWindowPos ( _appWin , IntPtr . Zero ,
49+ screen . Bounds . X + screen . Bounds . Width / 2 ,
50+ screen . Bounds . Y + screen . Bounds . Height / 2 ,
51+ 800 , 600 , NativeMethods . SWP_NOZORDER | NativeMethods . SWP_NOACTIVATE ) ;
52+
53+ // 250 ms gives the process's message loop time to dequeue and handle
54+ // WM_DPICHANGED, which Windows delivers asynchronously for cross-thread windows.
55+ await Task . Delay ( 250 ) ;
56+
57+ if ( ! IsConnected || _appWin == IntPtr . Zero )
58+ return ;
59+
60+ NativeMethods . SetParent ( _appWin , WindowHost . Handle ) ;
61+ ResizeEmbeddedWindow ( ) ;
62+ NativeMethods . ShowWindow ( _appWin , NativeMethods . WindowShowStyle . ShowNoActivate ) ;
63+ }
64+ finally
3365 {
34- var dpiX = ( int ) e . NewDpi . PixelsPerInchX ;
35- var dpiY = ( int ) e . NewDpi . PixelsPerInchY ;
36- var wParam = new IntPtr ( unchecked ( ( int ) ( uint ) ( ( dpiX & 0xFFFF ) | ( ( dpiY & 0xFFFF ) << 16 ) ) ) ) ;
37- NativeMethods . PostMessage ( _appWin , ( uint ) NativeMethods . WM . DPICHANGED , wParam , IntPtr . Zero ) ;
66+ _isDpiChanging = false ;
3867 }
3968 }
4069
0 commit comments