diff --git a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/DpiHelper.cs b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/DpiHelper.cs
index f944b0faf6d..83bd34cf2ec 100644
--- a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/DpiHelper.cs
+++ b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/DpiHelper.cs
@@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
+using Microsoft.Win32;
using static Interop;
namespace System.Windows.Forms
@@ -171,6 +172,45 @@ public static Bitmap CreateScaledBitmap(Bitmap logicalImage, int deviceDpi = 0)
///
public static bool IsScalingRequired => DeviceDpi != LogicalDpi;
+ ///
+ /// Retrieve the text scale factor, which is set via Settings > Display > Make Text Bigger.
+ /// The settings are stored in the registry under HKCU\Software\Microsoft\Accessibility in (DWORD)TextScaleFactor.
+ ///
+ /// The scaling factor in the range [1.0, 2.25].
+ /// Windows Text scaling
+ public static float GetTextScaleFactor()
+ {
+ // The default(100) and max(225) text scale factor is value what Settings display text scale
+ // applies and also clamps the text scale factor value between 100 and 225 value.
+ const short MinTextScaleValue = 100;
+ const short MaxTextScaleValue = 225;
+
+ short textScaleValue = MinTextScaleValue;
+ try
+ {
+ RegistryKey? key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Accessibility");
+ if (key is not null && key.GetValue("TextScaleFactor") is int _textScaleValue)
+ {
+ textScaleValue = (short)_textScaleValue;
+ }
+ }
+ catch
+ {
+ // Failed to read the registry for whatever reason.
+#if DEBUG
+ throw;
+#endif
+ }
+
+ // Restore the text scale if it isn't the default value in the valid text scale factor value
+ if (textScaleValue > MinTextScaleValue && textScaleValue <= MaxTextScaleValue)
+ {
+ return (float)textScaleValue / MinTextScaleValue;
+ }
+
+ return 1.0f;
+ }
+
///
/// scale logical pixel to the factor
///
diff --git a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/OsVersion.cs b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/OsVersion.cs
index b96ecce4dc8..33fe048b501 100644
--- a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/OsVersion.cs
+++ b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/OsVersion.cs
@@ -20,6 +20,12 @@ private static NtDll.RTL_OSVERSIONINFOEX InitVersion()
return info;
}
+ ///
+ /// Is Windows 10 first release or later. (Threshold 1, build 10240, version 1507)
+ ///
+ public static bool IsWindows10_1507OrGreater
+ => s_versionInfo.dwMajorVersion >= 10 && s_versionInfo.dwBuildNumber >= 10240;
+
///
/// Is Windows 10 Anniversary Update or later. (Redstone 1, build 14393, version 1607)
///
diff --git a/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt
index 3c6deb494bf..0657001a1b1 100644
--- a/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt
+++ b/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-~override System.Windows.Forms.MonthCalendar.OnResize(System.EventArgs e) -> void
\ No newline at end of file
+~override System.Windows.Forms.MonthCalendar.OnResize(System.EventArgs e) -> void
+~static System.Windows.Forms.Application.SetDefaultFont(System.Drawing.Font font) -> void
diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx
index e5bc1b57b65..c051093b38b 100644
--- a/src/System.Windows.Forms/src/Resources/SR.resx
+++ b/src/System.Windows.Forms/src/Resources/SR.resx
@@ -6495,7 +6495,7 @@ Stack trace where the illegal operation occurred was:
Width must be greater than MinWidth.
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.Setting event '{0}'
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
index 362c198fb45..90a8146930b 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
@@ -11052,8 +11052,8 @@ Trasování zásobníku, kde došlo k neplatné operaci:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- Před vytvořením prvního objektu IWin32Window v aplikaci je nutné volat metodu SetCompatibleTextRenderingDefault.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
index 12c2592c646..ff73b87403b 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
@@ -11052,8 +11052,8 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- SetCompatibleTextRenderingDefault muss aufgerufen werden, bevor das erste IWin32Window-Objekt in der Anwendung erstellt wird.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
index ac435b8567c..e622e06e7b7 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
@@ -11052,8 +11052,8 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- SetCompatibleTextRenderingDefault se debe llamar antes de crear el primer objeto IWin32Window en la aplicación.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
index 99405e21ac0..a82887a98c5 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
@@ -11052,8 +11052,8 @@ Cette opération non conforme s'est produite sur la trace de la pile :
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- SetCompatibleTextRenderingDefault doit être appelé avant la création du premier objet IWin32Window dans l'application.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
index 6eaf3f69621..85987eff1c5 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
@@ -11052,8 +11052,8 @@ Traccia dello stack da cui si è verificata l'operazione non valida:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- SetCompatibleTextRenderingDefault deve essere chiamato prima della creazione del primo oggetto IWin32Window nell'applicazione.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
index ee9ee6a80d6..cec37c236a1 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
@@ -11052,8 +11052,8 @@ Stack trace where the illegal operation occurred was:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- 最初の IWin32Window オブジェクトがアプリケーションで作成される前に、SetCompatibleTextRenderingDefault が呼び出されなければなりません。
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
index ceaaface994..8bd45205569 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
@@ -11052,8 +11052,8 @@ Stack trace where the illegal operation occurred was:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- SetCompatibleTextRenderingDefault는 애플리케이션에 첫 번째 IWin32Window 개체가 만들어지기 전에 호출해야 합니다.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
index 9093a31118f..5b3e6f28cb0 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
@@ -11052,8 +11052,8 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- Należy wywołać element SetCompatibleTextRenderingDefault, aby pierwszy obiekt IWin32Window został utworzony w aplikacji.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf
index a94a498efa5..491baf72140 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf
@@ -11052,8 +11052,8 @@ Rastreamento de pilha em que a operação ilegal ocorreu:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- É necessário chamar SetCompatibleTextRenderingDefault antes da criação do primeiro objeto IWin32Window no aplicativo.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
index 99c4d85470e..9bacf4d3f4c 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
@@ -11053,8 +11053,8 @@ Stack trace where the illegal operation occurred was:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- До создания первого объекта IWin32Window в приложении необходимо вызвать SetCompatibleTextRenderingDefault.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
index 66b07e9f414..e379cbcda73 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
@@ -11052,8 +11052,8 @@ Geçersiz işlemin gerçekleştiği yığın izi:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- Uygulamada ilk IWin32Window nesnesi oluşturulmadan önce SetCompatibleTextRenderingDefault çağrılmalıdır.
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf
index ece6a375883..99e50ea4134 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf
@@ -11052,8 +11052,8 @@ Stack trace where the illegal operation occurred was:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- 在应用程序中创建第一个 IWin32Window 对象之前,必须调用 SetCompatibleTextRenderingDefault。
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf
index 6668599a0b6..6749634b321 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf
@@ -11052,8 +11052,8 @@ Stack trace where the illegal operation occurred was:
- SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application.
- 在應用程式中建立第一個 IWin32Window 物件之前,必須先呼叫 SetCompatibleTextRenderingDefault。
+ {0} must be called before the first IWin32Window object is created in the application.
+ {0} must be called before the first IWin32Window object is created in the application.
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs
index b855665ab5f..a828226e093 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Diagnostics;
+using System.Drawing;
using System.Globalization;
using System.IO;
using System.Reflection;
@@ -30,6 +31,8 @@ public sealed partial class Application
/// Hash table for our event list
///
private static EventHandlerList s_eventHandlers;
+ private static Font s_defaultFont;
+ private static Font s_defaultFontScaled;
private static string s_startupPath;
private static string s_executablePath;
private static object s_appFileVersion;
@@ -279,6 +282,8 @@ public static InputLanguage CurrentInputLanguage
internal static bool CustomThreadExceptionHandlerAttached
=> ThreadContext.FromCurrent().CustomThreadExceptionHandlerAttached;
+ internal static Font DefaultFont => s_defaultFontScaled ?? s_defaultFont;
+
///
/// Gets the path for the executable file that started the application.
///
@@ -1218,6 +1223,31 @@ public static void Run(ApplicationContext context)
internal static void RunDialog(Form form)
=> ThreadContext.FromCurrent().RunMessageLoop(Interop.Mso.msoloop.ModalForm, new ModalApplicationContext(form));
+ ///
+ /// Scale the default font (if it is set) as per the Settings display text scale settings.
+ ///
+ internal static void ScaleDefaultFont()
+ {
+ if (s_defaultFont is null || !OsVersion.IsWindows10_1507OrGreater)
+ {
+ return;
+ }
+
+ float textScaleValue = DpiHelper.GetTextScaleFactor();
+
+ if (s_defaultFontScaled is not null)
+ {
+ s_defaultFontScaled.Dispose();
+ s_defaultFontScaled = null;
+ }
+
+ // Restore the text scale if it isn't the default value in the valid text scale factor value
+ if (textScaleValue > 1.0f)
+ {
+ s_defaultFontScaled = new Font(s_defaultFont.FontFamily, s_defaultFont.Size * textScaleValue);
+ }
+ }
+
///
/// Sets the static UseCompatibleTextRenderingDefault field on Control to the value passed in.
/// This switch determines the default text rendering engine to use by some controls that support
@@ -1227,12 +1257,60 @@ public static void SetCompatibleTextRenderingDefault(bool defaultValue)
{
if (NativeWindow.AnyHandleCreated)
{
- throw new InvalidOperationException(SR.Win32WindowAlreadyCreated);
+ throw new InvalidOperationException(string.Format(SR.Win32WindowAlreadyCreated, nameof(SetCompatibleTextRenderingDefault)));
}
Control.UseCompatibleTextRenderingDefault = defaultValue;
}
+ ///
+ /// Sets the default for process.
+ ///
+ /// The font to be used as a default across the application.
+ /// is .
+ ///
+ /// You can only call this method before the first window is created by your Windows Forms application.
+ ///
+ ///
+ ///
+ /// The system text scale factor will be applied to the font, i.e. if the default font is set to "Calibri, 11f"
+ /// and the text scale factor is set to 150% the resulting default font will be set to "Calibri, 16.5f".
+ ///
+ ///
+ /// Users can adjust text scale with the Make text bigger slider on the Settings -> Ease of Access -> Vision/Display screen.
+ ///
+ ///
+ /// Windows Text scaling
+ public static void SetDefaultFont(Font font)
+ {
+ if (font is null)
+ throw new ArgumentNullException(nameof(font));
+
+ if (NativeWindow.AnyHandleCreated)
+ throw new InvalidOperationException(string.Format(SR.Win32WindowAlreadyCreated, nameof(SetDefaultFont)));
+
+ // If user made a prior call to this API with a different custom fonts, we want to clean it up.
+ if (s_defaultFont is not null)
+ {
+ s_defaultFont?.Dispose();
+ s_defaultFont = null;
+ s_defaultFontScaled?.Dispose();
+ s_defaultFontScaled = null;
+ }
+
+ if (font.IsSystemFont)
+ {
+ // The system font is managed the .NET runtime, and it is already scaled to the current text scale factor.
+ // We need to clone it because our reference will no longer be scaled by the .NET runtime.
+ s_defaultFont = (Font)font.Clone();
+ }
+ else
+ {
+ s_defaultFont = font;
+ ScaleDefaultFont();
+ }
+ }
+
///
/// Sets the mode for process.
///
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 02fb8d840b0..36fc961ddf9 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
@@ -1810,7 +1810,7 @@ public static Font DefaultFont
{
if (s_defaultFont is null)
{
- s_defaultFont = SystemFonts.MessageBoxFont;
+ s_defaultFont = Application.DefaultFont ?? SystemFonts.MessageBoxFont;
Debug.Assert(s_defaultFont is not null, "defaultFont wasn't set!");
}
@@ -11603,6 +11603,7 @@ private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs
if (pref.Category == UserPreferenceCategory.Color)
{
s_defaultFont = null;
+ Application.ScaleDefaultFont();
OnSystemColorsChanged(EventArgs.Empty);
}
}
diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.Designer.cs b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.Designer.cs
index 33426fd4c48..2e3e6bbc79b 100644
--- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.Designer.cs
+++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.Designer.cs
@@ -47,7 +47,7 @@ private void InitializeComponent()
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(570, 30);
this.Controls.Add(this.flowLayoutPanelUITypeEditors);
this.Name = "MainForm";
diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.cs b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.cs
index f30dacfb62d..02ed97ff49b 100644
--- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.cs
+++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MainForm.cs
@@ -4,10 +4,12 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Forms.IntegrationTests.Common;
+using Microsoft.Win32;
using WindowsFormsApp1;
using WinformsControlsTest.UserControls;
@@ -29,8 +31,8 @@ public MainForm()
InitInfo info = buttonsInitInfo[item];
Button button = new Button
{
+ AutoSizeMode = AutoSizeMode.GrowAndShrink,
Name = info.Name,
- Size = new Size(259, 23),
TabIndex = (int)item,
Text = info.Name,
UseVisualStyleBackColor = true
@@ -40,15 +42,17 @@ public MainForm()
flowLayoutPanelUITypeEditors.Controls.Add(button);
}
- // Calculate the form size.
- ClientSize = new Size(545, 18 + (mainFormControlsTabOrderItems.Length + 1) / 2 * 29);
- MinimumSize = Size;
-
- // Force the panel to show all buttons
- flowLayoutPanelUITypeEditors.PerformLayout();
- flowLayoutPanelUITypeEditors.Controls[(int)MainFormControlsTabOrder.ButtonsButton].Focus();
-
Text = RuntimeInformation.FrameworkDescription;
+
+ SystemEvents.UserPreferenceChanged += (s, e) =>
+ {
+ // The default font gets reset for UserPreferenceCategory.Color
+ // though perhaps it should've been done for UserPreferenceCategory.Window
+ if (e.Category == UserPreferenceCategory.Color)
+ {
+ UpdateLayout();
+ }
+ };
}
private IReadOnlyDictionary GetButtonsInitInfo() => new Dictionary
@@ -159,6 +163,70 @@ public MainForm()
}
};
+ protected override void OnShown(EventArgs e)
+ {
+ base.OnShown(e);
+
+ UpdateLayout();
+ flowLayoutPanelUITypeEditors.Controls[(int)MainFormControlsTabOrder.ButtonsButton].Focus();
+ }
+
+ private void UpdateLayout()
+ {
+ MinimumSize = default;
+ Debug.WriteLine($"MessageBoxFont: {SystemFonts.MessageBoxFont}", nameof(MainForm));
+ Debug.WriteLine($"Default font: {Control.DefaultFont}", nameof(MainForm));
+
+ // 1. Auto-size all buttons
+ flowLayoutPanelUITypeEditors.SuspendLayout();
+ foreach (Control c in flowLayoutPanelUITypeEditors.Controls)
+ {
+ if (c is Button button)
+ {
+ button.AutoSize = true;
+ }
+ }
+
+ flowLayoutPanelUITypeEditors.ResumeLayout(true);
+
+ // 2. Find the biggest button
+ Size biggestButton = default;
+ foreach (Control c in flowLayoutPanelUITypeEditors.Controls)
+ {
+ if (c is Button button)
+ {
+ if (button.Width > biggestButton.Width)
+ {
+ biggestButton = button.Size;
+ }
+ }
+ }
+
+ Debug.WriteLine($"Biggest button size: {biggestButton}", nameof(MainForm));
+
+ // 3. Size all buttons to the biggest button
+ flowLayoutPanelUITypeEditors.SuspendLayout();
+ foreach (Control c in flowLayoutPanelUITypeEditors.Controls)
+ {
+ if (c is Button button)
+ {
+ button.AutoSize = false;
+ button.Size = biggestButton;
+ }
+ }
+
+ flowLayoutPanelUITypeEditors.ResumeLayout(true);
+
+ // 4. Calculate the new form size showing all buttons in two vertical columns
+ int padding = flowLayoutPanelUITypeEditors.Controls[0].Margin.All;
+ ClientSize = new Size(
+ (biggestButton.Width + padding * 2) * 2 + padding * 2,
+ (int)(flowLayoutPanelUITypeEditors.Controls.Count / 2 * (biggestButton.Height + padding * 2) + padding * 2)
+ );
+ MinimumSize = Size;
+ Debug.WriteLine($"Minimum form size: {MinimumSize}", nameof(MainForm));
+ }
+
private struct InitInfo
{
public InitInfo(string name, EventHandler handler)
diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.Designer.cs b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.Designer.cs
index 9ebd685d46b..cede22d53a3 100644
--- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.Designer.cs
+++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.Designer.cs
@@ -83,6 +83,9 @@ private void InitializeComponent()
//
// label1
//
+ this.label1.AccessibleDescription = "Test Label AccessibleDescription";
+ this.label1.AccessibleName = "Test Label AccessibleName";
+ this.label1.AccessibleRole = System.Windows.Forms.AccessibleRole.Indicator;
this.label1.AutoSize = true;
this.label1.FlatStyle = System.Windows.Forms.FlatStyle.System;
this.label1.Location = new System.Drawing.Point(13, 73);
@@ -90,9 +93,6 @@ private void InitializeComponent()
this.label1.Size = new System.Drawing.Size(35, 13);
this.label1.TabIndex = 2;
this.label1.Text = "label1";
- this.label1.AccessibleName = "Test Label AccessibleName";
- this.label1.AccessibleDescription = "Test Label AccessibleDescription";
- this.label1.AccessibleRole = System.Windows.Forms.AccessibleRole.Indicator;
//
// maskedTextBox1
//
@@ -196,6 +196,9 @@ private void InitializeComponent()
//
// groupBox1
//
+ this.groupBox1.AccessibleDescription = "Test GroupBox AccessibleDescription";
+ this.groupBox1.AccessibleName = "Test GroupBox AccessibleName";
+ this.groupBox1.AccessibleRole = System.Windows.Forms.AccessibleRole.Table;
this.groupBox1.Controls.Add(this.radioButton2);
this.groupBox1.Controls.Add(this.radioButton1);
this.groupBox1.Location = new System.Drawing.Point(125, 171);
@@ -204,9 +207,6 @@ private void InitializeComponent()
this.groupBox1.TabIndex = 7;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "groupBox1";
- this.groupBox1.AccessibleName = "Test GroupBox AccessibleName";
- this.groupBox1.AccessibleDescription = "Test GroupBox AccessibleDescription";
- this.groupBox1.AccessibleRole = System.Windows.Forms.AccessibleRole.Table;
//
// checkedListBox1
//
@@ -236,12 +236,12 @@ private void InitializeComponent()
this.domainUpDown1.Size = new System.Drawing.Size(120, 20);
this.domainUpDown1.TabIndex = 10;
this.domainUpDown1.Text = "domainUpDown1";
- //
- // Test3
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(629, 269);
+ //
+ // Form1
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(639, 397);
this.Controls.Add(this.domainUpDown1);
this.Controls.Add(this.numericUpDown1);
this.Controls.Add(this.checkedListBox1);
@@ -253,7 +253,7 @@ private void InitializeComponent()
this.Controls.Add(this.label1);
this.Controls.Add(this.button1);
this.Controls.Add(this.progressBar1);
- this.Name = "Test3";
+ this.Name = "Form1";
this.Text = "These look ok";
this.Load += new System.EventHandler(this.Test3_Load);
this.tabControl1.ResumeLayout(false);
diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.cs b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.cs
index c6620c65173..546a4f7e37d 100644
--- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.cs
+++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/MultipleControls.cs
@@ -4,6 +4,7 @@
using System;
using System.ComponentModel;
+using System.Drawing;
using System.Threading;
using System.Windows.Forms;
@@ -14,6 +15,107 @@ public partial class MultipleControls : Form
public MultipleControls()
{
InitializeComponent();
+ CreateMyListView();
+ }
+
+ private void CreateMyListView()
+ {
+ // Create a new ListView control.
+ ListView listView2 = new ListView
+ {
+ Bounds = new Rectangle(new Point(0, 0), new Size(400, 200)),
+
+ // Set the view to show details.
+ View = View.Details,
+ // Allow the user to edit item text.
+ LabelEdit = true,
+ // Allow the user to rearrange columns.
+ AllowColumnReorder = true,
+ // Display check boxes.
+ CheckBoxes = true,
+ // Select the item and subitems when selection is made.
+ FullRowSelect = true,
+ // Display grid lines.
+ GridLines = true,
+ // Sort the items in the list in ascending order.
+ Sorting = SortOrder.Ascending,
+
+ VirtualMode = true,
+ VirtualListSize = 3,
+ };
+
+ ListViewGroup listViewGroup1 = new("ListViewGroup", HorizontalAlignment.Left)
+ {
+ Header = "ListViewGroup",
+ Name = "listViewGroup1"
+ };
+ listView2.Groups.AddRange(new ListViewGroup[] { listViewGroup1 });
+
+ // Create three items and three sets of subitems for each item.
+ ListViewItem item1 = new("item1", 0)
+ {
+ // Place a check mark next to the item.
+ Checked = true
+ };
+ item1.SubItems.Add("sub1");
+ item1.SubItems.Add("sub2");
+ item1.SubItems.Add("sub3");
+ ListViewItem item2 = new("item2", 1);
+ item2.SubItems.Add("sub4");
+ item2.SubItems.Add("sub5");
+ item2.SubItems.Add("sub6");
+ ListViewItem item3 = new("item3", 0)
+ {
+ // Place a check mark next to the item.
+ Checked = true
+ };
+ item3.SubItems.Add("sub7");
+ item3.SubItems.Add("sub8");
+ item3.SubItems.Add("sub9");
+
+ // Add the items to the ListView, but because the listview is in Virtual Mode, we have to manage items ourselves
+ // and thus, we can't call the following:
+ // listView2.Items.AddRange(new ListViewItem[] { item1, item2, item3 });
+ listView2.RetrieveVirtualItem += (s, e) =>
+ {
+ e.Item = e.ItemIndex switch
+ {
+ 0 => item1,
+ 1 => item2,
+ 2 => item3,
+ _ => throw new IndexOutOfRangeException(),
+ };
+ };
+
+ // Create columns for the items and subitems.
+ // Width of -2 indicates auto-size.
+ listView2.Columns.Add("column1", "Item Column", -2, HorizontalAlignment.Left, 0);
+ listView2.Columns.Add("Column 2", -2, HorizontalAlignment.Left);
+ listView2.Columns.Add("Column 3", -2, HorizontalAlignment.Left);
+ listView2.Columns.Add("Column 4", -2, HorizontalAlignment.Center);
+ listView2.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
+
+ // Create two ImageList objects.
+ ImageList imageListSmall = new();
+ ImageList imageListLarge = new();
+
+ // Initialize the ImageList objects with bitmaps.
+ imageListSmall.Images.Add(Bitmap.FromFile("Images\\SmallA.bmp"));
+ imageListSmall.Images.Add(Bitmap.FromFile("Images\\SmallABlue.bmp"));
+ imageListLarge.Images.Add(Bitmap.FromFile("Images\\LargeA.bmp"));
+ imageListLarge.Images.Add(Bitmap.FromFile("Images\\LargeABlue.bmp"));
+
+ // Assign the ImageList objects to the ListView.
+ listView2.LargeImageList = imageListLarge;
+ listView2.SmallImageList = imageListSmall;
+
+ // Add the ListView to the control collection.
+ Controls.Add(listView2);
+ listView2.Dock = DockStyle.Bottom;
+
+ // Change a ListViewGroup's header.
+ listView2.Groups[0].HeaderAlignment = HorizontalAlignment.Center;
+ listView2.Groups[0].Header = "NewText";
}
private void Test3_Load(object sender, EventArgs e)
diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs
index 9c9b7d0f5cb..d93e5bf5770 100644
--- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs
+++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs
@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Globalization;
+using System.Drawing;
using System.Threading;
using System.Windows.Forms;
@@ -18,6 +18,12 @@ static class Program
static void Main()
{
Application.EnableVisualStyles();
+ Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
+
+ //Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8f));
+ //Application.SetDefaultFont(new Font(new FontFamily("Chiller"), 12f));
+ Application.SetDefaultFont(new Font(new FontFamily("Calibri"), 11f));
+
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); //UnhandledExceptionMode.ThrowException
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ApplicationTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ApplicationTests.cs
index 1a03bd95d29..055f8a3d566 100644
--- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ApplicationTests.cs
+++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ApplicationTests.cs
@@ -1,9 +1,10 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.ComponentModel;
+using System.Drawing;
using System.Globalization;
using System.IO;
using System.Threading;
@@ -151,6 +152,121 @@ public void Application_EnableVisualStyles_ManifestResourceExists()
Assert.NotNull(stream);
}
+ [WinFormsFact]
+ public void Application_DefaultFont_ReturnsNull_IfNoFontSet()
+ {
+ var applicationTestAccessor = typeof(Application).TestAccessor().Dynamic;
+ Assert.Null(applicationTestAccessor.s_defaultFont);
+ Assert.Null(applicationTestAccessor.s_defaultFontScaled);
+ Assert.Null(Application.DefaultFont);
+ }
+
+ [WinFormsFact]
+ public void Application_DefaultFont_Returns_DefaultFont_IfNotScaled()
+ {
+ var applicationTestAccessor = typeof(Application).TestAccessor().Dynamic;
+ Assert.Null(applicationTestAccessor.s_defaultFont);
+ Assert.Null(applicationTestAccessor.s_defaultFontScaled);
+
+ Font customFont = (Font)SystemFonts.CaptionFont.Clone();
+ try
+ {
+ applicationTestAccessor.s_defaultFont = customFont;
+
+ AreFontEqual(customFont, Application.DefaultFont);
+ }
+ finally
+ {
+ customFont.Dispose();
+ applicationTestAccessor.s_defaultFont = null;
+ applicationTestAccessor.s_defaultFontScaled?.Dispose();
+ applicationTestAccessor.s_defaultFontScaled = null;
+ }
+ }
+
+ [WinFormsFact]
+ public void Application_DefaultFont_Returns_ScaledDefaultFont_IfScaled()
+ {
+ var applicationTestAccessor = typeof(Application).TestAccessor().Dynamic;
+ Assert.Null(applicationTestAccessor.s_defaultFont);
+ Assert.Null(applicationTestAccessor.s_defaultFontScaled);
+
+ Font font = new Font(new FontFamily("Arial"), 12f);
+ Font scaled = new Font(new FontFamily("Arial"), 16f);
+ try
+ {
+ applicationTestAccessor.s_defaultFont = font;
+ applicationTestAccessor.s_defaultFontScaled = scaled;
+
+ AreFontEqual(scaled, Application.DefaultFont);
+ }
+ finally
+ {
+ applicationTestAccessor.s_defaultFont = null;
+ applicationTestAccessor.s_defaultFontScaled = null;
+ font.Dispose();
+ scaled.Dispose();
+ }
+ }
+
+ private static void AreFontEqual(Font expected, Font actual)
+ {
+ Assert.Equal(expected.Name, actual.Name);
+ Assert.Equal(expected.SizeInPoints, actual.SizeInPoints);
+ Assert.Equal(expected.GdiCharSet, actual.GdiCharSet);
+ Assert.Equal(expected.Style, actual.Style);
+ }
+
+ [WinFormsFact]
+ public void Application_SetDefaultFont_SetNull_ThrowsArgumentNullException()
+ {
+ Assert.Throws("font", () => Application.SetDefaultFont(null));
+ }
+
+ [WinFormsFact]
+ public void Application_SetDefaultFont_AfterHandleCreated_InvalidOperationException()
+ {
+ using var control = new Control();
+ var window = new NativeWindow();
+ window.AssignHandle(control.Handle);
+
+ Assert.Throws(() => Application.SetDefaultFont(SystemFonts.CaptionFont));
+ }
+
+ [WinFormsFact]
+ public void Application_SetDefaultFont_MustCloseSystemFont()
+ {
+ var applicationTestAccessor = typeof(Application).TestAccessor().Dynamic;
+ Assert.Null(applicationTestAccessor.s_defaultFont);
+ Assert.Null(applicationTestAccessor.s_defaultFontScaled);
+
+ Assert.True(SystemFonts.CaptionFont.IsSystemFont);
+
+ // This a unholy, but generally at this stage NativeWindow.AnyHandleCreated=true,
+ // And we won't be able to set the font, unless we flip the bit
+ var nativeWindowTestAccessor = typeof(NativeWindow).TestAccessor().Dynamic;
+ bool currentAnyHandleCreated = nativeWindowTestAccessor.t_anyHandleCreated;
+
+ try
+ {
+ nativeWindowTestAccessor.t_anyHandleCreated = false;
+
+ Application.SetDefaultFont(SystemFonts.CaptionFont);
+
+ Assert.False(applicationTestAccessor.s_defaultFont.IsSystemFont);
+ }
+ finally
+ {
+ // Flip the bit back
+ nativeWindowTestAccessor.t_anyHandleCreated = currentAnyHandleCreated;
+
+ applicationTestAccessor.s_defaultFont.Dispose();
+ applicationTestAccessor.s_defaultFontScaled?.Dispose();
+ applicationTestAccessor.s_defaultFont = null;
+ applicationTestAccessor.s_defaultFontScaled = null;
+ }
+ }
+
[WinFormsTheory]
[CommonMemberData(nameof(CommonTestHelper.GetEnumTypeTheoryDataInvalid), typeof(HighDpiMode))]
public void Application_SetHighDpiMode_SetInvalidValue_ThrowsInvalidEnumArgumentException(HighDpiMode value)