Skip to content

Commit 02a98c7

Browse files
authored
[release/9.0] Scale errorProvider Icon to DeviceDPI (#13079)
<!-- Please read CONTRIBUTING.md before submitting a pull request --> BackPort PR #12947 to 9.0 Fixes #12939 ## Proposed changes - Calculate the display size of the ErrorProvide Icon according to the DPI of the current device <!-- We are in TELL-MODE the following section must be completed --> ## Customer Impact - The icon of errorProvider is not scaled well on HDPI screen ## Regression? - Yes. it's fine in dotnet 8. This is due to #10850, the initial loaded size of the ErrorProvider IconRegion is scaled to 100% DPI even on HDPI screens. ## Risk - Low – because the change only change ErrorProvider icon's display size when it run in HDPI screen. <!-- end TELL-MODE --> ## Testing - Manual testing with the user-provided project ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/dotnet/winforms/pull/13079)
2 parents dbfe911 + 2837ea3 commit 02a98c7

File tree

4 files changed

+35
-17
lines changed

4 files changed

+35
-17
lines changed

src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,17 @@ internal static HighDpiMode GetThreadHighDpiMode()
419419
return HighDpiMode.DpiUnaware;
420420
}
421421

422+
/// <summary>
423+
/// Get X, Y metrics at DPI, IF icon is not already that size, create and return a new one.
424+
/// </summary>
425+
internal static Icon ScaleSmallIconToDpi(Icon icon, int dpi)
426+
{
427+
int width = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSMICON, (uint)dpi);
428+
int height = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSMICON, (uint)dpi);
429+
430+
return (icon.Width == width && icon.Height == height) ? icon : new(icon, width, height);
431+
}
432+
422433
/// <summary>
423434
/// Sets the requested DPI mode. If the current OS does not support the requested mode,
424435
/// </summary>

src/System.Windows.Forms/src/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorWindow.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,8 @@ protected override void WmDpiChangedBeforeParent(ref Message m)
454454
}
455455

456456
double factor = ((double)currentDpi) / _parent._deviceDpi;
457-
using Icon icon = _provider.Icon;
457+
Icon icon = _provider.Icon;
458+
_provider.CurrentDpi = currentDpi;
458459
_provider.Icon = new Icon(icon, (int)(icon.Width * factor), (int)(icon.Height * factor));
459460
}
460461
}

src/System.Windows.Forms/src/System/Windows/Forms/ErrorProvider/ErrorProvider.IconRegion.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ internal class IconRegion : IHandle<HICON>
1515
private Region? _region;
1616
private readonly Icon _icon;
1717

18-
public IconRegion(Icon icon)
18+
public IconRegion(Icon icon, int currentDpi)
1919
{
20-
_icon = new Icon(icon, ScaleHelper.LogicalSmallSystemIconSize);
20+
_icon = ScaleHelper.ScaleSmallIconToDpi(icon, currentDpi);
2121
}
2222

2323
/// <summary>

src/System.Windows.Forms/src/System/Windows/Forms/ErrorProvider/ErrorProvider.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ public partial class ErrorProvider : Component, IExtenderProvider, ISupportIniti
2424
{
2525
private readonly Dictionary<Control, ControlItem> _items = [];
2626
private readonly Dictionary<Control, ErrorWindow> _windows = [];
27-
private Icon _icon = DefaultIcon;
27+
private Icon? _icon;
2828
private IconRegion? _region;
29+
private int _currentDpi;
2930
private int _itemIdCounter;
3031
private int _blinkRate;
3132
private ErrorBlinkStyle _blinkStyle;
@@ -80,7 +81,6 @@ public ErrorProvider(IContainer container)
8081
: this()
8182
{
8283
ArgumentNullException.ThrowIfNull(container);
83-
8484
container.Add(this);
8585
}
8686

@@ -308,7 +308,7 @@ public object? DataSource
308308
{
309309
if (_parentControl is not null && _parentControl.BindingContext is not null && value is not null && !string.IsNullOrEmpty(_dataMember))
310310
{
311-
// Let's check if the datamember exists in the new data source
311+
// Let's check if the data member exists in the new data source
312312
try
313313
{
314314
_errorManager = _parentControl.BindingContext[value, _dataMember];
@@ -546,10 +546,8 @@ private static Icon DefaultIcon
546546
if (t_defaultIcon is null)
547547
{
548548
// Error provider uses small Icon.
549-
int width = PInvokeCore.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSMICON);
550-
int height = PInvokeCore.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSMICON);
551-
using Icon defaultIcon = new(typeof(ErrorProvider), "Error");
552-
t_defaultIcon = new Icon(defaultIcon, width, height);
549+
Icon defaultIcon = new(typeof(ErrorProvider), "Error");
550+
t_defaultIcon = ScaleHelper.ScaleSmallIconToDpi(defaultIcon, ScaleHelper.InitialSystemDpi);
553551
}
554552
}
555553

@@ -567,10 +565,7 @@ private static Icon DefaultIcon
567565
[SRDescription(nameof(SR.ErrorProviderIconDescr))]
568566
public Icon Icon
569567
{
570-
get
571-
{
572-
return _icon;
573-
}
568+
get => _icon ??= DefaultIcon;
574569
set
575570
{
576571
_icon = value.OrThrowIfNull();
@@ -583,10 +578,21 @@ public Icon Icon
583578
}
584579
}
585580

581+
/// <summary>
582+
/// Gets or sets the DPI at which the current error is displayed.
583+
/// If currentDpi is not set, it defaults to _parentControl.DeviceDpi
584+
/// or the system DPI.
585+
/// </summary>
586+
private int CurrentDpi
587+
{
588+
get => _currentDpi != 0 ? _currentDpi : _parentControl?.DeviceDpi ?? ScaleHelper.InitialSystemDpi;
589+
set => _currentDpi = value;
590+
}
591+
586592
/// <summary>
587593
/// Create the icon region on demand.
588594
/// </summary>
589-
internal IconRegion Region => _region ??= new IconRegion(Icon);
595+
internal IconRegion Region => _region ??= new IconRegion(Icon, CurrentDpi);
590596

591597
/// <summary>
592598
/// Begin bulk member initialization - deferring binding to data source until EndInit is reached
@@ -761,7 +767,7 @@ internal ErrorWindow EnsureErrorWindow(Control parent)
761767
[SRDescription(nameof(SR.ErrorProviderIconPaddingDescr))]
762768
public int GetIconPadding(Control control) => EnsureControlItem(control).IconPadding;
763769

764-
private void ResetIcon() => Icon = DefaultIcon;
770+
private void ResetIcon() => _icon = null;
765771

766772
[EditorBrowsable(EditorBrowsableState.Advanced)]
767773
protected virtual void OnRightToLeftChanged(EventArgs e)
@@ -814,5 +820,5 @@ public void SetIconPadding(Control control, int padding)
814820
EnsureControlItem(control).IconPadding = padding;
815821
}
816822

817-
private bool ShouldSerializeIcon() => Icon != DefaultIcon;
823+
private bool ShouldSerializeIcon() => _icon is not null && _icon != DefaultIcon;
818824
}

0 commit comments

Comments
 (0)