diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ContainerControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ContainerControl.cs index 67bf3e42056..22f19297fcc 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ContainerControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ContainerControl.cs @@ -868,7 +868,7 @@ protected override void OnFontChanged(EventArgs e) // to scale in Dpi mode (during WM_DPICHANGED event). // This may require scaling/relayout of the form. AutoScaleFactor will take // AutoScaleMode into account while scaling the controls. - if (AutoScaleMode != AutoScaleMode.None) + if (AutoScaleMode != AutoScaleMode.None && IsHandleCreated) { _currentAutoScaleDimensions = SizeF.Empty; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index 1b214a78510..f56735480f6 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -2094,40 +2094,7 @@ public virtual Font Font [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ActiveXFontMarshaler))] get { - // if application is in PermonitorV2 mode and font is scaled when moved between monitors. - // ToDo: need to work on getting Font serialization at design time right. - if (ScaledControlFont is not null) - { - return ScaledControlFont; - } - - if (TryGetExplicitlySetFont(out Font font)) - { - return font; - } - - font = GetParentFont(); - if (font is not null) - { - return font; - } - - if (IsActiveX) - { - font = ActiveXAmbientFont; - if (font is not null) - { - return font; - } - } - - AmbientProperties ambient = AmbientPropertiesService; - if (ambient is not null && ambient.Font is not null) - { - return ambient.Font; - } - - return DefaultFont; + return GetCurrentFontAndDpi(out _); } [param: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ActiveXFontMarshaler))] @@ -2162,8 +2129,6 @@ public virtual Font Font // Cleanup any font handle wrapper... DisposeFontHandle(); - ScaledControlFont = value; - if (Properties.ContainsInteger(s_fontHeightProperty)) { Properties.SetInteger(s_fontHeightProperty, (value is null) ? -1 : value.Height); @@ -2192,7 +2157,7 @@ internal Gdi32.HFONT FontHandle { get { - // if application is in PermonitorV2 mode and font is scaled when application moved between monitor. + // if application is in PerMonitorV2 mode and font is scaled when application moved between monitor. if (ScaledControlFont is not null) { if (_scaledFontWrapper is null) @@ -2366,11 +2331,12 @@ public virtual Color ForeColor remove => Events.RemoveHandler(s_foreColorEvent, value); } - private Font GetParentFont() + private Font GetParentFont(out int fontDpi) { + fontDpi = _deviceDpi; if (ParentInternal is not null && ParentInternal.CanAccessProperties) { - return ParentInternal.Font; + return ParentInternal.GetCurrentFontAndDpi(out fontDpi); } else { @@ -5790,7 +5756,7 @@ protected virtual Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, Boun RECT adornmentsAfterDpiChange = default; CreateParams cp = CreateParams; - // We would need to get adornments metrics for both (old and new) Dpi in case application is in permonitor V2 mode and Dpi changed. + // We would need to get adornments metrics for both (old and new) Dpi in case application is in PerMonitorV2 mode and Dpi changed. AdjustWindowRectExForControlDpi(ref adornmentsAfterDpiChange, cp.Style, bMenu: false, cp.ExStyle); if (_oldDeviceDpi != _deviceDpi && OsVersion.IsWindows10_1703OrGreater) @@ -5948,6 +5914,52 @@ internal virtual Control GetFirstChildControlInTabOrder(bool forward) return found; } + /// + /// Gets the control . If the font is inherited, traverse through the parent hierarchy and retreives the font. + /// + /// Dpi of the control for which is evaluated. + /// + /// The control's + /// + internal Font GetCurrentFontAndDpi(out int fontDpi) + { + fontDpi = _deviceDpi; + + // If application is in PerMonitorV2 mode and font is scaled when moved between monitors. + if (ScaledControlFont is not null) + { + return ScaledControlFont; + } + + if (TryGetExplicitlySetFont(out Font font)) + { + return font; + } + + font = GetParentFont(out fontDpi); + if (font is not null) + { + return font; + } + + if (IsActiveX) + { + font = ActiveXAmbientFont; + if (font is not null) + { + return font; + } + } + + AmbientProperties ambient = AmbientPropertiesService; + if (ambient is not null && ambient.Font is not null) + { + return ambient.Font; + } + + return DefaultFont; + } + private protected virtual IList GetNeighboringToolsRectangles() => ((IKeyboardToolTip)ToolStripControlHost)?.GetNeighboringToolsRectangles() ?? GetOwnNeighboringToolsRectangles(); @@ -7777,12 +7789,29 @@ protected virtual void OnHandleCreated(EventArgs e) SetWindowFont(); } - if (DpiHelper.IsPerMonitorV2Awareness && !(typeof(Form).IsAssignableFrom(GetType()))) + if (DpiHelper.IsPerMonitorV2Awareness) { int old = _deviceDpi; + Font localFont = GetCurrentFontAndDpi(out int fontDpi); _deviceDpi = (int)User32.GetDpiForWindow(this); if (old != _deviceDpi) { + if (fontDpi != _deviceDpi) + { + // Controls are by default font scaled. + // Dpi change requires font to be recalculated in order to get controls scaled with right dpi. + var factor = (float)_deviceDpi / fontDpi; + ScaledControlFont = localFont.WithSize(localFont.Size * factor); + + // If it is a container control that inherit Font and is scaled by parent, we simply scale Font + // and wait for OnFontChangedEvent caused by its parent. Otherwise, we scale Font and trigger + // 'OnFontChanged' event explicitly. ex: winforms designer natively hosted in VS. + if (TryGetExplicitlySetFont(out Font local) && local is not null) + { + Font = ScaledControlFont; + } + } + RescaleConstantsForDpi(old, _deviceDpi); } } @@ -12213,48 +12242,49 @@ private void WmDpiChangedBeforeParent(ref Message m) { DefWndProc(ref m); - if (IsHandleCreated) + _oldDeviceDpi = _deviceDpi; + + // In order to support tests, will be querying Dpi from the message first. + int newDeviceDpi = PARAM.SignedLOWORD(m.WParam); + + // On certain OS versions, for non-test scenarios, WParam may be empty. + if (newDeviceDpi == 0) { - _oldDeviceDpi = _deviceDpi; + newDeviceDpi = (int)User32.GetDpiForWindow(this); + } - // In order to support tests, will be querying Dpi from the message first. - _deviceDpi = PARAM.SignedLOWORD(m.WParam); + if (_oldDeviceDpi == newDeviceDpi) + { + OnDpiChangedBeforeParent(EventArgs.Empty); + return; + } - // On certain OS versions, for non-test scenarios, WParam may be empty. - if (_deviceDpi == 0) - { - _deviceDpi = (int)User32.GetDpiForWindow(this); - } + Font localFont = GetCurrentFontAndDpi(out int fontDpi); + _deviceDpi = newDeviceDpi; - // Controls are by default font scaled. - // Dpi change requires font to be recalculated in order to get controls scaled with right dpi. - if (_oldDeviceDpi != _deviceDpi) - { - var factor = (float)_deviceDpi / _oldDeviceDpi; - Font localFont = Font; - Font scaledFont = localFont.WithSize(localFont.Size * factor); + if (fontDpi == _deviceDpi) + { + OnDpiChangedBeforeParent(EventArgs.Empty); + return; + } - // If it is a container control that inherit Font and is scaled by parent, we simply scale Font - // and wait for OnFontChangedEvent caused by its parent. Otherwise, we scale Font and trigger - // 'OnFontChanged' event explicitly. ex: winforms designer in VS. - if (TryGetExplicitlySetFont(out Font local) || this is not ContainerControl || !IsScaledByParent(this)) - { - if (local is not null) - { - Font = scaledFont; - } - else - { - ScaledControlFont = scaledFont; - } + // Controls are by default font scaled. + // Dpi change requires font to be recalculated in order to get controls scaled with right dpi. + var factor = (float)_deviceDpi / fontDpi; + ScaledControlFont?.Dispose(); + ScaledControlFont = localFont.WithSize(localFont.Size * factor); - RescaleConstantsForDpi(_oldDeviceDpi, _deviceDpi); - } - else - { - ScaledControlFont = scaledFont; - } + // If it is a container control that inherit Font and is scaled by parent, we simply scale Font + // and wait for OnFontChangedEvent caused by its parent. Otherwise, we scale Font and trigger + // 'OnFontChanged' event explicitly. ex: winforms designer in VS. + if (TryGetExplicitlySetFont(out Font local) || this is not ContainerControl || !IsScaledByParent(this)) + { + if (local is not null) + { + Font = ScaledControlFont; } + + RescaleConstantsForDpi(_oldDeviceDpi, _deviceDpi); } OnDpiChangedBeforeParent(EventArgs.Empty);