From cbb3bccc8d52e46ac7fe0d99a35d83afe019005a Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Wed, 4 Feb 2026 18:06:21 +0800 Subject: [PATCH 1/7] Add theming API and improve selection/grid readability --- .../PublicAPI.Unshipped.txt | 13 +++++ .../DarkThemeExtensions.DataGridView.cs | 40 +++++++++++++ .../Windows/Forms/Theming/DarkThemePalette.cs | 56 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs create mode 100644 src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 3348c917404..b272c115bbc 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -2,6 +2,8 @@ static System.Windows.Forms.Application.SetColorMode(System.Windows.Forms.System static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterScreen) -> System.Threading.Tasks.Task! +static System.Windows.Forms.Theming.DarkThemeExtensions.ApplyDarkTheme(this System.Windows.Forms.DataGridView! dataGridView) -> void +static System.Windows.Forms.Theming.DarkThemePalette.CreateDefault() -> System.Windows.Forms.Theming.DarkThemePalette System.Windows.Forms.ControlStyles.ApplyThemingImplicitly = 524288 -> System.Windows.Forms.ControlStyles System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler? @@ -19,6 +21,17 @@ System.Windows.Forms.SystemColorMode System.Windows.Forms.SystemColorMode.Classic = 0 -> System.Windows.Forms.SystemColorMode System.Windows.Forms.SystemColorMode.Dark = 2 -> System.Windows.Forms.SystemColorMode System.Windows.Forms.SystemColorMode.System = 1 -> System.Windows.Forms.SystemColorMode +System.Windows.Forms.Theming.DarkThemeExtensions +System.Windows.Forms.Theming.DarkThemePalette +System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette() -> void +System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette(System.Drawing.Color surface, System.Drawing.Color onSurface, System.Drawing.Color headerBackgroundColor, System.Drawing.Color headerForegroundColor, System.Drawing.Color selectionBackgroundColor, System.Drawing.Color selectionForegroundColor, System.Drawing.Color grid) -> void +System.Windows.Forms.Theming.DarkThemePalette.Grid.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.HeaderBackgroundColor.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.HeaderForegroundColor.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.OnSurface.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.SelectionBackgroundColor.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.SelectionForegroundColor.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.Surface.get -> System.Drawing.Color System.Windows.Forms.VisualStyles.ComboBoxState.Focused = 5 -> System.Windows.Forms.VisualStyles.ComboBoxState virtual System.Windows.Forms.Form.OnFormBorderColorChanged(System.EventArgs! e) -> void virtual System.Windows.Forms.Form.OnFormCaptionBackColorChanged(System.EventArgs! e) -> void diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs new file mode 100644 index 00000000000..5ff00f0372f --- /dev/null +++ b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.Theming; + +public static class DarkThemeExtensions +{ + public static void ApplyDarkTheme(this DataGridView dataGridView) + { + ArgumentNullException.ThrowIfNull(dataGridView); + + var palette = DarkThemePalette.CreateDefault(); + + // Disable the header's system theme so that our custom styles are not overridden by the system. + dataGridView.EnableHeadersVisualStyles = false; + + // Table body + dataGridView.BackgroundColor = palette.Surface; + dataGridView.DefaultCellStyle.BackColor = palette.Surface; + dataGridView.DefaultCellStyle.ForeColor = palette.OnSurface; + + // Column header + dataGridView.ColumnHeadersDefaultCellStyle.BackColor = palette.HeaderBackgroundColor; + dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = palette.HeaderForegroundColor; + + // Light-colored dividing line + dataGridView.GridColor = ControlPaint.Light(palette.Surface, 0.50f); + + // RowHeaders + dataGridView.RowHeadersDefaultCellStyle.BackColor = palette.HeaderBackgroundColor; + dataGridView.RowHeadersDefaultCellStyle.ForeColor = palette.HeaderForegroundColor; + + // Selected state + dataGridView.RowHeadersDefaultCellStyle.SelectionBackColor = Color.Empty; + dataGridView.ColumnHeadersDefaultCellStyle.SelectionBackColor = Color.Empty; + + dataGridView.DefaultCellStyle.SelectionBackColor = palette.SelectionBackgroundColor; + dataGridView.DefaultCellStyle.SelectionForeColor = palette.SelectionForegroundColor; + } +} diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs new file mode 100644 index 00000000000..740e16363e4 --- /dev/null +++ b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; + +namespace System.Windows.Forms.Theming; + +/// +/// A minimal, immutable palette for Dark Mode theming of DataGridView. +/// +public readonly struct DarkThemePalette +{ + public Color Surface { get; } + public Color OnSurface { get; } + public Color HeaderBackgroundColor { get; } + public Color HeaderForegroundColor { get; } + public Color SelectionBackgroundColor { get; } + public Color SelectionForegroundColor { get; } + public Color Grid { get; } + + public DarkThemePalette( + Color surface, + Color onSurface, + Color headerBackgroundColor, + Color headerForegroundColor, + Color selectionBackgroundColor, + Color selectionForegroundColor, + Color grid) + { + Surface = surface; + OnSurface = onSurface; + HeaderBackgroundColor = headerBackgroundColor; + HeaderForegroundColor = headerForegroundColor; + SelectionBackgroundColor = selectionBackgroundColor; + SelectionForegroundColor = selectionForegroundColor; + Grid = grid; + } + + /// + /// Creates a conservative default palette that follows SystemColors in Dark Mode + /// and meets contrast requirements for selection states. + /// + public static DarkThemePalette CreateDefault() + { + var surface = SystemColors.Window; + var onSurface = SystemColors.WindowText; + var headerBg = SystemColors.ControlDarkDark; + var headerFg = SystemColors.ActiveCaptionText; + var selectionBg = Color.FromArgb(0x33, 0x66, 0xCC); + var selectionFg = Color.White; + var grid = ControlPaint.Dark(surface, 0.6f); + + return new DarkThemePalette( + surface, onSurface, headerBg, headerFg, selectionBg, selectionFg, grid); + } +} From 0466578be939759d1511c7174d949d54b89c29d1 Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Thu, 5 Feb 2026 15:32:28 +0800 Subject: [PATCH 2/7] Update DarkThemePalette --- src/System.Windows.Forms/PublicAPI.Unshipped.txt | 3 +-- .../Resources/System/Windows/Forms/Theming/DarkThemePalette.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index b272c115bbc..205b694a001 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -3,7 +3,7 @@ static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Wi static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterScreen) -> System.Threading.Tasks.Task! static System.Windows.Forms.Theming.DarkThemeExtensions.ApplyDarkTheme(this System.Windows.Forms.DataGridView! dataGridView) -> void -static System.Windows.Forms.Theming.DarkThemePalette.CreateDefault() -> System.Windows.Forms.Theming.DarkThemePalette +static System.Windows.Forms.Theming.DarkThemePalette.CreateDefault() -> System.Windows.Forms.Theming.DarkThemePalette! System.Windows.Forms.ControlStyles.ApplyThemingImplicitly = 524288 -> System.Windows.Forms.ControlStyles System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler? @@ -23,7 +23,6 @@ System.Windows.Forms.SystemColorMode.Dark = 2 -> System.Windows.Forms.SystemColo System.Windows.Forms.SystemColorMode.System = 1 -> System.Windows.Forms.SystemColorMode System.Windows.Forms.Theming.DarkThemeExtensions System.Windows.Forms.Theming.DarkThemePalette -System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette() -> void System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette(System.Drawing.Color surface, System.Drawing.Color onSurface, System.Drawing.Color headerBackgroundColor, System.Drawing.Color headerForegroundColor, System.Drawing.Color selectionBackgroundColor, System.Drawing.Color selectionForegroundColor, System.Drawing.Color grid) -> void System.Windows.Forms.Theming.DarkThemePalette.Grid.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.HeaderBackgroundColor.get -> System.Drawing.Color diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs index 740e16363e4..4111644bc40 100644 --- a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs +++ b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs @@ -8,7 +8,7 @@ namespace System.Windows.Forms.Theming; /// /// A minimal, immutable palette for Dark Mode theming of DataGridView. /// -public readonly struct DarkThemePalette +public class DarkThemePalette { public Color Surface { get; } public Color OnSurface { get; } From 0e46f9df6015256e71de06926c4c1570f78798d8 Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Mon, 9 Feb 2026 17:55:28 +0800 Subject: [PATCH 3/7] Update the sorting icon in the column headers so it appears white in Dark Mode. --- .../PublicAPI.Unshipped.txt | 5 +- .../DarkThemeExtensions.DataGridView.cs | 6 + .../Windows/Forms/Theming/DarkThemePalette.cs | 41 ++- .../DataGridView/DataGridView.Methods.cs | 14 + .../Controls/DataGridView/DataGridViewCell.cs | 12 + .../DataGridView/DataGridViewCellStyle.cs | 32 +- .../DataGridViewColumnHeaderCell.cs | 335 ++++++++++-------- 7 files changed, 289 insertions(+), 156 deletions(-) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 205b694a001..74862d9f600 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -5,6 +5,8 @@ static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.Task static System.Windows.Forms.Theming.DarkThemeExtensions.ApplyDarkTheme(this System.Windows.Forms.DataGridView! dataGridView) -> void static System.Windows.Forms.Theming.DarkThemePalette.CreateDefault() -> System.Windows.Forms.Theming.DarkThemePalette! System.Windows.Forms.ControlStyles.ApplyThemingImplicitly = 524288 -> System.Windows.Forms.ControlStyles +System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.get -> System.Drawing.Color +System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.set -> void System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionTextColorChanged -> System.EventHandler? @@ -23,13 +25,14 @@ System.Windows.Forms.SystemColorMode.Dark = 2 -> System.Windows.Forms.SystemColo System.Windows.Forms.SystemColorMode.System = 1 -> System.Windows.Forms.SystemColorMode System.Windows.Forms.Theming.DarkThemeExtensions System.Windows.Forms.Theming.DarkThemePalette -System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette(System.Drawing.Color surface, System.Drawing.Color onSurface, System.Drawing.Color headerBackgroundColor, System.Drawing.Color headerForegroundColor, System.Drawing.Color selectionBackgroundColor, System.Drawing.Color selectionForegroundColor, System.Drawing.Color grid) -> void +System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette(System.Drawing.Color surface, System.Drawing.Color onSurface, System.Drawing.Color headerBackgroundColor, System.Drawing.Color headerForegroundColor, System.Drawing.Color selectionBackgroundColor, System.Drawing.Color selectionForegroundColor, System.Drawing.Color grid, System.Drawing.Color sortGlyphColor) -> void System.Windows.Forms.Theming.DarkThemePalette.Grid.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.HeaderBackgroundColor.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.HeaderForegroundColor.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.OnSurface.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.SelectionBackgroundColor.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.SelectionForegroundColor.get -> System.Drawing.Color +System.Windows.Forms.Theming.DarkThemePalette.SortGlyphColor.get -> System.Drawing.Color System.Windows.Forms.Theming.DarkThemePalette.Surface.get -> System.Drawing.Color System.Windows.Forms.VisualStyles.ComboBoxState.Focused = 5 -> System.Windows.Forms.VisualStyles.ComboBoxState virtual System.Windows.Forms.Form.OnFormBorderColorChanged(System.EventArgs! e) -> void diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs index 5ff00f0372f..43f7294027a 100644 --- a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs +++ b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs @@ -7,6 +7,11 @@ public static class DarkThemeExtensions { public static void ApplyDarkTheme(this DataGridView dataGridView) { + if (!Application.IsDarkModeEnabled) + { + return; + } + ArgumentNullException.ThrowIfNull(dataGridView); var palette = DarkThemePalette.CreateDefault(); @@ -22,6 +27,7 @@ public static void ApplyDarkTheme(this DataGridView dataGridView) // Column header dataGridView.ColumnHeadersDefaultCellStyle.BackColor = palette.HeaderBackgroundColor; dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = palette.HeaderForegroundColor; + dataGridView.ColumnHeadersDefaultCellStyle.SortGlyphColor = palette.SortGlyphColor; // Light-colored dividing line dataGridView.GridColor = ControlPaint.Light(palette.Surface, 0.50f); diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs index 4111644bc40..d9c6bdc9c15 100644 --- a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs +++ b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs @@ -10,14 +10,46 @@ namespace System.Windows.Forms.Theming; /// public class DarkThemePalette { + /// + /// Gets the surface (background) color. + /// public Color Surface { get; } + + /// + /// Gets the on-surface (foreground/text) color. + /// public Color OnSurface { get; } + + /// + /// Gets the header background color. + /// public Color HeaderBackgroundColor { get; } + + /// + /// Gets the header foreground color. + /// public Color HeaderForegroundColor { get; } + + /// + /// Gets the selection background color. + /// public Color SelectionBackgroundColor { get; } + + /// + /// Gets the selection foreground color. + /// public Color SelectionForegroundColor { get; } + + /// + /// Gets the grid line color. + /// public Color Grid { get; } + /// + /// Gets the sort glyph (arrow) color for column headers. + /// + public Color SortGlyphColor { get; } + public DarkThemePalette( Color surface, Color onSurface, @@ -25,7 +57,8 @@ public DarkThemePalette( Color headerForegroundColor, Color selectionBackgroundColor, Color selectionForegroundColor, - Color grid) + Color grid, + Color sortGlyphColor) { Surface = surface; OnSurface = onSurface; @@ -34,6 +67,7 @@ public DarkThemePalette( SelectionBackgroundColor = selectionBackgroundColor; SelectionForegroundColor = selectionForegroundColor; Grid = grid; + SortGlyphColor = sortGlyphColor; } /// @@ -50,7 +84,10 @@ public static DarkThemePalette CreateDefault() var selectionFg = Color.White; var grid = ControlPaint.Dark(surface, 0.6f); + // Use a light gray color for sort glyph that provides good contrast on dark backgrounds + Color sortGlyph = Color.White; + return new DarkThemePalette( - surface, onSurface, headerBg, headerFg, selectionBg, selectionFg, grid); + surface, onSurface, headerBg, headerFg, selectionBg, selectionFg, grid, sortGlyph); } } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs index c48810dbc1a..04d03973c1d 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs @@ -2853,6 +2853,20 @@ private void BuildInheritedColumnHeaderCellStyle(DataGridViewCellStyle inherited inheritedCellStyle.SelectionForeColor = dataGridViewStyle.SelectionForeColor; } + // Inherit SortGlyphColor for column header sort arrow + if (cellStyle is not null && !cellStyle.SortGlyphColor.IsEmpty) + { + inheritedCellStyle.SortGlyphColor = cellStyle.SortGlyphColor; + } + else if (!columnHeadersStyle.SortGlyphColor.IsEmpty) + { + inheritedCellStyle.SortGlyphColor = columnHeadersStyle.SortGlyphColor; + } + else if (!dataGridViewStyle.SortGlyphColor.IsEmpty) + { + inheritedCellStyle.SortGlyphColor = dataGridViewStyle.SortGlyphColor; + } + inheritedCellStyle.Font = cellStyle?.Font ?? columnHeadersStyle.Font ?? dataGridViewStyle.Font; if (cellStyle is not null && !cellStyle.IsNullValueDefault) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCell.cs index a7f26a45ae3..fb2556e9a3f 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCell.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCell.cs @@ -1435,6 +1435,18 @@ private static Bitmap GetBitmap(string bitmapName) => ? ControlPaint.LightLight(baseline) : SystemColors.ControlLightLight; } + else if (Application.IsDarkModeEnabled) + { + // In Dark Mode, use higher contrast colors for better visibility. + // For dark backgrounds, we need lighter colors that stand out. + darkColor = darkDistance < ContrastThreshold + ? ControlPaint.Light(baseline, 0.5f) + : SystemColors.ControlLight; + + lightColor = lightDistance < ContrastThreshold + ? ControlPaint.LightLight(baseline) + : SystemColors.ControlLightLight; + } else { darkColor = darkDistance < ContrastThreshold diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCellStyle.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCellStyle.cs index 31bcde3c355..f7471b58ce5 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCellStyle.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCellStyle.cs @@ -23,6 +23,7 @@ public class DataGridViewCellStyle : ICloneable private static readonly int s_propPadding = PropertyStore.CreateKey(); private static readonly int s_propSelectionBackColor = PropertyStore.CreateKey(); private static readonly int s_propSelectionForeColor = PropertyStore.CreateKey(); + private static readonly int s_propSortGlyphColor = PropertyStore.CreateKey(); private static readonly int s_propTag = PropertyStore.CreateKey(); private static readonly int s_propWrapMode = PropertyStore.CreateKey(); private DataGridView? _dataGridView; @@ -59,6 +60,7 @@ public DataGridViewCellStyle(DataGridViewCellStyle dataGridViewCellStyle) WrapModeInternal = dataGridViewCellStyle.WrapMode; Tag = dataGridViewCellStyle.Tag; PaddingInternal = dataGridViewCellStyle.Padding; + SortGlyphColor = dataGridViewCellStyle.SortGlyphColor; } [SRDescription(nameof(SR.DataGridViewCellStyleAlignmentDescr))] @@ -331,6 +333,25 @@ public Color SelectionForeColor } } + /// + /// Gets or sets the color used to draw the sort glyph (arrow) in column headers. + /// When set to , the glyph color is automatically + /// calculated based on the background color for optimal contrast. + /// + [SRCategory(nameof(SR.CatAppearance))] + public Color SortGlyphColor + { + get => Properties.GetValueOrDefault(s_propSortGlyphColor); + set + { + Color previous = Properties.AddOrRemoveValue(s_propSortGlyphColor, value); + if (!previous.Equals(value)) + { + OnPropertyChanged(DataGridViewCellStylePropertyInternal.Color); + } + } + } + [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object? Tag @@ -439,6 +460,11 @@ public virtual void ApplyStyle(DataGridViewCellStyle dataGridViewCellStyle) { PaddingInternal = dataGridViewCellStyle.Padding; } + + if (!dataGridViewCellStyle.SortGlyphColor.IsEmpty) + { + SortGlyphColor = dataGridViewCellStyle.SortGlyphColor; + } } public virtual DataGridViewCellStyle Clone() => new(this); @@ -465,7 +491,8 @@ internal DataGridViewCellStyleDifferences GetDifferencesFrom(DataGridViewCellSty dgvcs.BackColor != BackColor || dgvcs.ForeColor != ForeColor || dgvcs.SelectionBackColor != SelectionBackColor || - dgvcs.SelectionForeColor != SelectionForeColor); + dgvcs.SelectionForeColor != SelectionForeColor || + dgvcs.SortGlyphColor != SortGlyphColor); if (preferredSizeAffectingPropDifferent) { @@ -492,6 +519,7 @@ public override int GetHashCode() hash.Add(ForeColor); hash.Add(SelectionBackColor); hash.Add(SelectionForeColor); + hash.Add(SortGlyphColor); hash.Add(Font); hash.Add(NullValue); hash.Add(DataSourceNullValue); @@ -530,6 +558,8 @@ internal void RemoveScope(DataGridViewCellStyleScopes scope) private bool ShouldSerializeSelectionForeColor() => Properties.ContainsKey(s_propSelectionForeColor); + private bool ShouldSerializeSortGlyphColor() => Properties.ContainsKey(s_propSortGlyphColor); + public override string ToString() { StringBuilder sb = new(128); diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs index 047158c8631..7bd21c7b62a 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs @@ -1001,175 +1001,206 @@ private Rectangle PaintPrivate( if (paint && displaySortGlyph && PaintContentBackground(paintParts)) { - (Color darkColor, Color lightColor) = GetContrastedColors(cellStyle.BackColor); + Color darkColor; + Color lightColor; + bool useFlatStyle = false; + + // Use explicit SortGlyphColor if set, otherwise calculate contrasted colors + if (!cellStyle.SortGlyphColor.IsEmpty) + { + // Use the same color for both to create a flat (non-3D) appearance + darkColor = cellStyle.SortGlyphColor; + lightColor = cellStyle.SortGlyphColor; + // Force flat style when custom SortGlyphColor is set for solid fill + useFlatStyle = true; + } + else + { + (darkColor, lightColor) = GetContrastedColors(cellStyle.BackColor); + } + using var penControlDark = darkColor.GetCachedPenScope(); using var penControlLightLight = lightColor.GetCachedPenScope(); + // Determine if we should use flat style (solid fill) or 3D style (outline) + bool isFlatLook = useFlatStyle || (advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.OutsetPartial && + advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.OutsetDouble && + advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.Outset && + advancedBorderStyle.Right != DataGridViewAdvancedCellBorderStyle.Inset); + if (SortGlyphDirection == SortOrder.Ascending) { - switch (advancedBorderStyle.Right) + if (isFlatLook) { - case DataGridViewAdvancedCellBorderStyle.OutsetPartial: - case DataGridViewAdvancedCellBorderStyle.OutsetDouble: - case DataGridViewAdvancedCellBorderStyle.Outset: - // Sunken look - g.DrawLine(penControlDark, - sortGlyphLocation.X, - sortGlyphLocation.Y + s_sortGlyphHeight - 2, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y); - g.DrawLine(penControlDark, - sortGlyphLocation.X + 1, - sortGlyphLocation.Y + s_sortGlyphHeight - 2, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 2); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y, - sortGlyphLocation.X + s_sortGlyphWidth - 3, - sortGlyphLocation.Y + s_sortGlyphHeight - 2); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X, - sortGlyphLocation.Y + s_sortGlyphHeight - 1, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 1); - break; - - case DataGridViewAdvancedCellBorderStyle.Inset: - // Raised look - g.DrawLine(penControlLightLight, - sortGlyphLocation.X, - sortGlyphLocation.Y + s_sortGlyphHeight - 2, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X + 1, - sortGlyphLocation.Y + s_sortGlyphHeight - 2, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y); - g.DrawLine(penControlDark, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 2); - g.DrawLine(penControlDark, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y, - sortGlyphLocation.X + s_sortGlyphWidth - 3, - sortGlyphLocation.Y + s_sortGlyphHeight - 2); + // Flat look - solid filled triangle + for (int line = 0; line < s_sortGlyphWidth / 2; line++) + { g.DrawLine(penControlDark, - sortGlyphLocation.X, - sortGlyphLocation.Y + s_sortGlyphHeight - 1, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 1); - break; - - default: - // Flat look - for (int line = 0; line < s_sortGlyphWidth / 2; line++) - { + sortGlyphLocation.X + line, + sortGlyphLocation.Y + s_sortGlyphHeight - line - 1, + sortGlyphLocation.X + s_sortGlyphWidth - line - 1, + sortGlyphLocation.Y + s_sortGlyphHeight - line - 1); + } + + g.DrawLine(penControlDark, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphHeight - s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphHeight - s_sortGlyphWidth / 2); + } + else + { + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + case DataGridViewAdvancedCellBorderStyle.Outset: + // Sunken look g.DrawLine(penControlDark, - sortGlyphLocation.X + line, - sortGlyphLocation.Y + s_sortGlyphHeight - line - 1, - sortGlyphLocation.X + s_sortGlyphWidth - line - 1, - sortGlyphLocation.Y + s_sortGlyphHeight - line - 1); - } + sortGlyphLocation.X, + sortGlyphLocation.Y + s_sortGlyphHeight - 2, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y); + g.DrawLine(penControlDark, + sortGlyphLocation.X + 1, + sortGlyphLocation.Y + s_sortGlyphHeight - 2, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 2); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y, + sortGlyphLocation.X + s_sortGlyphWidth - 3, + sortGlyphLocation.Y + s_sortGlyphHeight - 2); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y + s_sortGlyphHeight - 1, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 1); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + // Raised look + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y + s_sortGlyphHeight - 2, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + 1, + sortGlyphLocation.Y + s_sortGlyphHeight - 2, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y); + g.DrawLine(penControlDark, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 2); + g.DrawLine(penControlDark, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y, + sortGlyphLocation.X + s_sortGlyphWidth - 3, + sortGlyphLocation.Y + s_sortGlyphHeight - 2); + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y + s_sortGlyphHeight - 1, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 1); + break; - g.DrawLine(penControlDark, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphHeight - s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphHeight - s_sortGlyphWidth / 2); - break; + } } } else { Debug.Assert(SortGlyphDirection == SortOrder.Descending); - switch (advancedBorderStyle.Right) + if (isFlatLook) { - case DataGridViewAdvancedCellBorderStyle.OutsetPartial: - case DataGridViewAdvancedCellBorderStyle.OutsetDouble: - case DataGridViewAdvancedCellBorderStyle.Outset: - // Sunken look - g.DrawLine(penControlDark, - sortGlyphLocation.X, - sortGlyphLocation.Y + 1, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y + s_sortGlyphHeight - 1); - g.DrawLine(penControlDark, - sortGlyphLocation.X + 1, - sortGlyphLocation.Y + 1, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y + s_sortGlyphHeight - 1); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 1, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y + 1); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 1, - sortGlyphLocation.X + s_sortGlyphWidth - 3, - sortGlyphLocation.Y + 1); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X, - sortGlyphLocation.Y, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y); - break; - - case DataGridViewAdvancedCellBorderStyle.Inset: - // Raised look - g.DrawLine(penControlLightLight, - sortGlyphLocation.X, - sortGlyphLocation.Y + 1, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y + s_sortGlyphHeight - 1); - g.DrawLine(penControlLightLight, - sortGlyphLocation.X + 1, - sortGlyphLocation.Y + 1, - sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, - sortGlyphLocation.Y + s_sortGlyphHeight - 1); - g.DrawLine(penControlDark, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 1, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y + 1); - g.DrawLine(penControlDark, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphHeight - 1, - sortGlyphLocation.X + s_sortGlyphWidth - 3, - sortGlyphLocation.Y + 1); + // Flat look - solid filled triangle (pointing down) + for (int line = 0; line < s_sortGlyphWidth / 2; line++) + { g.DrawLine(penControlDark, - sortGlyphLocation.X, - sortGlyphLocation.Y, - sortGlyphLocation.X + s_sortGlyphWidth - 2, - sortGlyphLocation.Y); - break; - - default: - // Flat look - for (int line = 0; line < s_sortGlyphWidth / 2; line++) - { - g.DrawLine(penControlDark, - sortGlyphLocation.X + line, - sortGlyphLocation.Y + line + 2, - sortGlyphLocation.X + s_sortGlyphWidth - line - 1, - sortGlyphLocation.Y + line + 2); - } + sortGlyphLocation.X + line, + sortGlyphLocation.Y + line + 2, + sortGlyphLocation.X + s_sortGlyphWidth - line - 1, + sortGlyphLocation.Y + line + 2); + } - g.DrawLine(penControlDark, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphWidth / 2 + 1, - sortGlyphLocation.X + s_sortGlyphWidth / 2, - sortGlyphLocation.Y + s_sortGlyphWidth / 2 + 2); - break; + g.DrawLine(penControlDark, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphWidth / 2 + 1, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphWidth / 2 + 2); + } + else + { + Debug.Assert(SortGlyphDirection == SortOrder.Descending); + switch (advancedBorderStyle.Right) + { + case DataGridViewAdvancedCellBorderStyle.OutsetPartial: + case DataGridViewAdvancedCellBorderStyle.OutsetDouble: + case DataGridViewAdvancedCellBorderStyle.Outset: + // Sunken look + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y + 1, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y + s_sortGlyphHeight - 1); + g.DrawLine(penControlDark, + sortGlyphLocation.X + 1, + sortGlyphLocation.Y + 1, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y + s_sortGlyphHeight - 1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 1, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y + 1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 1, + sortGlyphLocation.X + s_sortGlyphWidth - 3, + sortGlyphLocation.Y + 1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y); + break; + + case DataGridViewAdvancedCellBorderStyle.Inset: + // Raised look + g.DrawLine(penControlLightLight, + sortGlyphLocation.X, + sortGlyphLocation.Y + 1, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y + s_sortGlyphHeight - 1); + g.DrawLine(penControlLightLight, + sortGlyphLocation.X + 1, + sortGlyphLocation.Y + 1, + sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1, + sortGlyphLocation.Y + s_sortGlyphHeight - 1); + g.DrawLine(penControlDark, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 1, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y + 1); + g.DrawLine(penControlDark, + sortGlyphLocation.X + s_sortGlyphWidth / 2, + sortGlyphLocation.Y + s_sortGlyphHeight - 1, + sortGlyphLocation.X + s_sortGlyphWidth - 3, + sortGlyphLocation.Y + 1); + g.DrawLine(penControlDark, + sortGlyphLocation.X, + sortGlyphLocation.Y, + sortGlyphLocation.X + s_sortGlyphWidth - 2, + sortGlyphLocation.Y); + break; + } } } } From 20ce8c5ff7c8ae4e816a93c04dfa73b6e06f3893 Mon Sep 17 00:00:00 2001 From: Leaf Shi <132890443+LeafShi1@users.noreply.github.com> Date: Tue, 10 Feb 2026 09:40:53 +0800 Subject: [PATCH 4/7] Remove unnecessary line break in DataGridViewColumnHeaderCell --- .../Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs index 7bd21c7b62a..f8ecc2cc95d 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs @@ -1111,7 +1111,6 @@ private Rectangle PaintPrivate( sortGlyphLocation.X + s_sortGlyphWidth - 2, sortGlyphLocation.Y + s_sortGlyphHeight - 1); break; - } } } From cc7aa788c674e6129b92604356b932b095229a3d Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Tue, 10 Feb 2026 15:40:30 +0800 Subject: [PATCH 5/7] Update ComboBoxCell and CheckBoxCell --- .../Windows/Forms/Theming/DarkThemePalette.cs | 14 +++++----- .../DataGridView/DataGridViewCheckBoxCell.cs | 7 ++--- ...oxCell.DataGridViewComboBoxCellRenderer.cs | 26 ++++++++++++++++++- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs index d9c6bdc9c15..e4ed2f067d0 100644 --- a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs +++ b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs @@ -76,13 +76,13 @@ public DarkThemePalette( /// public static DarkThemePalette CreateDefault() { - var surface = SystemColors.Window; - var onSurface = SystemColors.WindowText; - var headerBg = SystemColors.ControlDarkDark; - var headerFg = SystemColors.ActiveCaptionText; - var selectionBg = Color.FromArgb(0x33, 0x66, 0xCC); - var selectionFg = Color.White; - var grid = ControlPaint.Dark(surface, 0.6f); + Color surface = SystemColors.Window; + Color onSurface = SystemColors.WindowText; + Color headerBg = SystemColors.ControlDarkDark; + Color headerFg = SystemColors.ActiveCaptionText; + Color selectionBg = Color.FromArgb(0x33, 0x66, 0xCC); + Color selectionFg = Color.White; + Color grid = ControlPaint.Dark(surface, 0.6f); // Use a light gray color for sort glyph that provides good contrast on dark backgrounds Color sortGlyph = Color.White; diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCheckBoxCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCheckBoxCell.cs index eeeaac207eb..8dcea8d9030 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCheckBoxCell.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCheckBoxCell.cs @@ -1240,10 +1240,11 @@ private Rectangle PaintPrivate( { if (paint && PaintContentForeground(paintParts)) { - DataGridViewCheckBoxCellRenderer.DrawCheckBox( + CheckBoxRenderer.DrawCheckBoxWithVisualStyles( g, - new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height), - (int)themeCheckBoxState); + new Point(checkBoxX, checkBoxY), + themeCheckBoxState, + DataGridView.HWNDInternal); } resultBounds = new Rectangle(checkBoxX, checkBoxY, checkBoxSize.Width, checkBoxSize.Height); diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewComboBoxCell.DataGridViewComboBoxCellRenderer.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewComboBoxCell.DataGridViewComboBoxCellRenderer.cs index 7c5cf83af53..97ef39bdc90 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewComboBoxCell.DataGridViewComboBoxCellRenderer.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewComboBoxCell.DataGridViewComboBoxCellRenderer.cs @@ -17,6 +17,11 @@ private static class DataGridViewComboBoxCellRenderer private static readonly VisualStyleElement s_comboBoxDropDownButtonLeft = VisualStyleElement.ComboBox.DropDownButtonLeft.Normal; private static readonly VisualStyleElement s_comboBoxReadOnlyButton = VisualStyleElement.ComboBox.ReadOnlyButton.Normal; + // Dark Mode element for drop-down button (same as ComboBoxRenderer uses) + private static VisualStyleElement ComboBoxDropDownButtonElement => Application.IsDarkModeEnabled + ? VisualStyleElement.CreateElement($"{Control.DarkModeIdentifier}_{Control.ComboBoxButtonThemeIdentifier}::{Control.ComboboxClassIdentifier}", 1, 1) + : VisualStyleElement.ComboBox.DropDownButton.Normal; + public static VisualStyleRenderer VisualStyleRenderer { get @@ -54,7 +59,12 @@ public static void DrawBorder(Graphics g, Rectangle bounds) public static void DrawDropDownButton(Graphics g, Rectangle bounds, ComboBoxState state, bool rightToLeft) { - if (rightToLeft) + // Use Dark Mode element when enabled + if (Application.IsDarkModeEnabled) + { + InitializeRenderer(ComboBoxDropDownButtonElement, (int)state); + } + else if (rightToLeft) { InitializeRenderer(s_comboBoxDropDownButtonLeft, (int)state); } @@ -68,6 +78,20 @@ public static void DrawDropDownButton(Graphics g, Rectangle bounds, ComboBoxStat public static void DrawReadOnlyButton(Graphics g, Rectangle bounds, ComboBoxState state) { + // Use Dark Mode element when enabled + if (Application.IsDarkModeEnabled) + { + // Draw dark background similar to ComboBox in Dark Mode + using var brush = new SolidBrush(Color.FromArgb(45, 45, 45)); + g.FillRectangle(brush, bounds); + + // Draw border + using var pen = new Pen(Color.FromArgb(100, 100, 100)); + g.DrawRectangle(pen, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1); + + return; + } + InitializeRenderer(s_comboBoxReadOnlyButton, (int)state); t_visualStyleRenderer.DrawBackground(g, bounds); From 2af28f4c30abfdb49d084f30e5474737705b326e Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Thu, 12 Feb 2026 17:34:27 +0800 Subject: [PATCH 6/7] Add quirk AppContextSwitches.DataGridViewDarkModeTheming --- .../LocalAppContextSwitches.cs | 23 +++++ .../PublicAPI.Unshipped.txt | 15 --- .../DarkThemeExtensions.DataGridView.cs | 46 --------- .../Windows/Forms/Theming/DarkThemePalette.cs | 93 ------------------- .../DataGridView/DataGridView.Methods.cs | 48 ++++++++++ .../DataGridViewColumnHeaderCell.cs | 11 +-- .../DataGridView/DataGridViewLinkCell.cs | 40 ++++++++ .../Forms/Controls/Labels/LinkUtilities.cs | 15 +++ 8 files changed, 130 insertions(+), 161 deletions(-) delete mode 100644 src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs delete mode 100644 src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs diff --git a/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs b/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs index 5b728b3472a..effd0dd4629 100644 --- a/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs +++ b/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs @@ -26,6 +26,7 @@ internal static partial class LocalAppContextSwitches internal const string EnableMsoComponentManagerSwitchName = "Switch.System.Windows.Forms.EnableMsoComponentManager"; internal const string TreeNodeCollectionAddRangeRespectsSortOrderSwitchName = "System.Windows.Forms.TreeNodeCollectionAddRangeRespectsSortOrder"; internal const string MoveTreeViewTextLocationOnePixelSwitchName = "System.Windows.Forms.TreeView.MoveTreeViewTextLocationOnePixel"; + internal const string DataGridViewDarkModeThemingSwitchName = "System.Windows.Forms.DataGridViewDarkModeTheming"; private static int s_scaleTopLevelFormMinMaxSizeForDpi; private static int s_anchorLayoutV2; @@ -39,6 +40,7 @@ internal static partial class LocalAppContextSwitches private static int s_treeNodeCollectionAddRangeRespectsSortOrder; private static int s_moveTreeViewTextLocationOnePixel; + private static int s_dataGridViewDarkModeTheming; private static FrameworkName? s_targetFrameworkName; @@ -134,6 +136,16 @@ private static bool GetSwitchDefaultValue(string switchName) } } + if (framework.Version.Major >= 10) + { + // Behavior changes added in .NET 10 + + if (switchName == DataGridViewDarkModeThemingSwitchName) + { + return true; + } + } + return false; } @@ -231,4 +243,15 @@ public static bool MoveTreeViewTextLocationOnePixel [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetCachedSwitchValue(MoveTreeViewTextLocationOnePixelSwitchName, ref s_moveTreeViewTextLocationOnePixel); } + + /// + /// Indicates whether dark mode theming is automatically applied to DataGridView + /// controls when the application is running in dark mode. Defaults to + /// for .NET 10+ applications. Set to to opt out. + /// + public static bool DataGridViewDarkModeTheming + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue(DataGridViewDarkModeThemingSwitchName, ref s_dataGridViewDarkModeTheming); + } } diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 74862d9f600..3348c917404 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -2,11 +2,7 @@ static System.Windows.Forms.Application.SetColorMode(System.Windows.Forms.System static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterScreen) -> System.Threading.Tasks.Task! -static System.Windows.Forms.Theming.DarkThemeExtensions.ApplyDarkTheme(this System.Windows.Forms.DataGridView! dataGridView) -> void -static System.Windows.Forms.Theming.DarkThemePalette.CreateDefault() -> System.Windows.Forms.Theming.DarkThemePalette! System.Windows.Forms.ControlStyles.ApplyThemingImplicitly = 524288 -> System.Windows.Forms.ControlStyles -System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.get -> System.Drawing.Color -System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.set -> void System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionTextColorChanged -> System.EventHandler? @@ -23,17 +19,6 @@ System.Windows.Forms.SystemColorMode System.Windows.Forms.SystemColorMode.Classic = 0 -> System.Windows.Forms.SystemColorMode System.Windows.Forms.SystemColorMode.Dark = 2 -> System.Windows.Forms.SystemColorMode System.Windows.Forms.SystemColorMode.System = 1 -> System.Windows.Forms.SystemColorMode -System.Windows.Forms.Theming.DarkThemeExtensions -System.Windows.Forms.Theming.DarkThemePalette -System.Windows.Forms.Theming.DarkThemePalette.DarkThemePalette(System.Drawing.Color surface, System.Drawing.Color onSurface, System.Drawing.Color headerBackgroundColor, System.Drawing.Color headerForegroundColor, System.Drawing.Color selectionBackgroundColor, System.Drawing.Color selectionForegroundColor, System.Drawing.Color grid, System.Drawing.Color sortGlyphColor) -> void -System.Windows.Forms.Theming.DarkThemePalette.Grid.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.HeaderBackgroundColor.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.HeaderForegroundColor.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.OnSurface.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.SelectionBackgroundColor.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.SelectionForegroundColor.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.SortGlyphColor.get -> System.Drawing.Color -System.Windows.Forms.Theming.DarkThemePalette.Surface.get -> System.Drawing.Color System.Windows.Forms.VisualStyles.ComboBoxState.Focused = 5 -> System.Windows.Forms.VisualStyles.ComboBoxState virtual System.Windows.Forms.Form.OnFormBorderColorChanged(System.EventArgs! e) -> void virtual System.Windows.Forms.Form.OnFormCaptionBackColorChanged(System.EventArgs! e) -> void diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs deleted file mode 100644 index 43f7294027a..00000000000 --- a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemeExtensions.DataGridView.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Windows.Forms.Theming; - -public static class DarkThemeExtensions -{ - public static void ApplyDarkTheme(this DataGridView dataGridView) - { - if (!Application.IsDarkModeEnabled) - { - return; - } - - ArgumentNullException.ThrowIfNull(dataGridView); - - var palette = DarkThemePalette.CreateDefault(); - - // Disable the header's system theme so that our custom styles are not overridden by the system. - dataGridView.EnableHeadersVisualStyles = false; - - // Table body - dataGridView.BackgroundColor = palette.Surface; - dataGridView.DefaultCellStyle.BackColor = palette.Surface; - dataGridView.DefaultCellStyle.ForeColor = palette.OnSurface; - - // Column header - dataGridView.ColumnHeadersDefaultCellStyle.BackColor = palette.HeaderBackgroundColor; - dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = palette.HeaderForegroundColor; - dataGridView.ColumnHeadersDefaultCellStyle.SortGlyphColor = palette.SortGlyphColor; - - // Light-colored dividing line - dataGridView.GridColor = ControlPaint.Light(palette.Surface, 0.50f); - - // RowHeaders - dataGridView.RowHeadersDefaultCellStyle.BackColor = palette.HeaderBackgroundColor; - dataGridView.RowHeadersDefaultCellStyle.ForeColor = palette.HeaderForegroundColor; - - // Selected state - dataGridView.RowHeadersDefaultCellStyle.SelectionBackColor = Color.Empty; - dataGridView.ColumnHeadersDefaultCellStyle.SelectionBackColor = Color.Empty; - - dataGridView.DefaultCellStyle.SelectionBackColor = palette.SelectionBackgroundColor; - dataGridView.DefaultCellStyle.SelectionForeColor = palette.SelectionForegroundColor; - } -} diff --git a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs b/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs deleted file mode 100644 index e4ed2f067d0..00000000000 --- a/src/System.Windows.Forms/Resources/System/Windows/Forms/Theming/DarkThemePalette.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Drawing; - -namespace System.Windows.Forms.Theming; - -/// -/// A minimal, immutable palette for Dark Mode theming of DataGridView. -/// -public class DarkThemePalette -{ - /// - /// Gets the surface (background) color. - /// - public Color Surface { get; } - - /// - /// Gets the on-surface (foreground/text) color. - /// - public Color OnSurface { get; } - - /// - /// Gets the header background color. - /// - public Color HeaderBackgroundColor { get; } - - /// - /// Gets the header foreground color. - /// - public Color HeaderForegroundColor { get; } - - /// - /// Gets the selection background color. - /// - public Color SelectionBackgroundColor { get; } - - /// - /// Gets the selection foreground color. - /// - public Color SelectionForegroundColor { get; } - - /// - /// Gets the grid line color. - /// - public Color Grid { get; } - - /// - /// Gets the sort glyph (arrow) color for column headers. - /// - public Color SortGlyphColor { get; } - - public DarkThemePalette( - Color surface, - Color onSurface, - Color headerBackgroundColor, - Color headerForegroundColor, - Color selectionBackgroundColor, - Color selectionForegroundColor, - Color grid, - Color sortGlyphColor) - { - Surface = surface; - OnSurface = onSurface; - HeaderBackgroundColor = headerBackgroundColor; - HeaderForegroundColor = headerForegroundColor; - SelectionBackgroundColor = selectionBackgroundColor; - SelectionForegroundColor = selectionForegroundColor; - Grid = grid; - SortGlyphColor = sortGlyphColor; - } - - /// - /// Creates a conservative default palette that follows SystemColors in Dark Mode - /// and meets contrast requirements for selection states. - /// - public static DarkThemePalette CreateDefault() - { - Color surface = SystemColors.Window; - Color onSurface = SystemColors.WindowText; - Color headerBg = SystemColors.ControlDarkDark; - Color headerFg = SystemColors.ActiveCaptionText; - Color selectionBg = Color.FromArgb(0x33, 0x66, 0xCC); - Color selectionFg = Color.White; - Color grid = ControlPaint.Dark(surface, 0.6f); - - // Use a light gray color for sort glyph that provides good contrast on dark backgrounds - Color sortGlyph = Color.White; - - return new DarkThemePalette( - surface, onSurface, headerBg, headerFg, selectionBg, selectionFg, grid, sortGlyph); - } -} diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs index 04d03973c1d..c04b417b925 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs @@ -17,6 +17,48 @@ namespace System.Windows.Forms; public partial class DataGridView { + /// + /// Applies dark mode theming to the DataGridView when the application is running + /// in dark mode. This is called automatically from + /// when the System.Windows.Forms.DataGridViewDarkModeTheming AppContext switch + /// is enabled (default for .NET 10+). + /// + private void ApplyDarkModeTheming() + { + Color surface = SystemColors.Window; + Color onSurface = SystemColors.WindowText; + Color headerBg = SystemColors.ControlDarkDark; + Color headerFg = SystemColors.ActiveCaptionText; + Color selectionBg = Color.FromArgb(0x33, 0x66, 0xCC); + Color selectionFg = Color.White; + + // Disable the header's system theme so that our custom styles are not overridden by the system. + EnableHeadersVisualStyles = false; + + // Table body + BackgroundColor = surface; + DefaultCellStyle.BackColor = surface; + DefaultCellStyle.ForeColor = onSurface; + + // Column headers + ColumnHeadersDefaultCellStyle.BackColor = headerBg; + ColumnHeadersDefaultCellStyle.ForeColor = headerFg; + + // Light-colored dividing line + GridColor = ControlPaint.Light(surface, 0.50f); + + // Row headers + RowHeadersDefaultCellStyle.BackColor = headerBg; + RowHeadersDefaultCellStyle.ForeColor = headerFg; + + // Selected state - use Color.Empty so header selection follows body selection + RowHeadersDefaultCellStyle.SelectionBackColor = Color.Empty; + ColumnHeadersDefaultCellStyle.SelectionBackColor = Color.Empty; + + DefaultCellStyle.SelectionBackColor = selectionBg; + DefaultCellStyle.SelectionForeColor = selectionFg; + } + protected virtual void AccessibilityNotifyCurrentCellChanged(Point cellAddress) { if (cellAddress.X < 0 || cellAddress.X >= Columns.Count) @@ -15256,6 +15298,12 @@ protected override void OnHandleCreated(EventArgs e) OnGlobalAutoSize(); } + // Automatically apply dark mode theming when enabled via quirk switch. + if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming) + { + ApplyDarkModeTheming(); + } + SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged; } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs index f8ecc2cc95d..8063c751933 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs @@ -1005,13 +1005,11 @@ private Rectangle PaintPrivate( Color lightColor; bool useFlatStyle = false; - // Use explicit SortGlyphColor if set, otherwise calculate contrasted colors - if (!cellStyle.SortGlyphColor.IsEmpty) + // In Dark Mode with quirk enabled, use a solid white glyph for better visibility + if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming) { - // Use the same color for both to create a flat (non-3D) appearance - darkColor = cellStyle.SortGlyphColor; - lightColor = cellStyle.SortGlyphColor; - // Force flat style when custom SortGlyphColor is set for solid fill + darkColor = Color.White; + lightColor = Color.White; useFlatStyle = true; } else @@ -1137,7 +1135,6 @@ private Rectangle PaintPrivate( } else { - Debug.Assert(SortGlyphDirection == SortOrder.Descending); switch (advancedBorderStyle.Right) { case DataGridViewAdvancedCellBorderStyle.OutsetPartial: diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs index 89195caa137..45f0fdd42f3 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs @@ -52,6 +52,10 @@ public Color ActiveLinkColor { return HighContrastLinkColor; } + else if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming) + { + return Selected ? SystemColors.HighlightText : LinkUtilities.DarkModeActiveLinkColor; + } else { // Return the default IE Color if cell is not not selected. @@ -160,6 +164,10 @@ public Color LinkColor { return HighContrastLinkColor; } + else if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming) + { + return DarkModeLinkColor; + } else { // Return the default IE Color when cell is not selected @@ -297,6 +305,10 @@ public Color VisitedLinkColor { return Selected ? SystemColors.HighlightText : LinkUtilities.GetVisitedLinkColor(); } + else if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming) + { + return DarkModeVisitedLinkColor; + } else { // Return the default IE Color if cell is not not selected @@ -357,6 +369,24 @@ private Color HighContrastLinkColor } } + /// + /// Gets a link color with sufficient contrast for dark mode backgrounds. + /// + private Color DarkModeLinkColor + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Selected ? SystemColors.HighlightText : LinkUtilities.DarkModeLinkColor; + } + + /// + /// Gets a visited link color with sufficient contrast for dark mode backgrounds. + /// + private Color DarkModeVisitedLinkColor + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Selected ? SystemColors.HighlightText : LinkUtilities.DarkModeVisitedLinkColor; + } + public override Type ValueType => base.ValueType ?? s_defaultValueType; public override object Clone() @@ -1014,6 +1044,16 @@ private Rectangle PaintPrivate( linkColor = LinkColor; } + // In dark mode, when the cell is painted as selected, force + // the link text to use SelectionForeColor so it contrasts + // with the custom selection background. + if (cellSelected + && Application.IsDarkModeEnabled + && AppContextSwitches.DataGridViewDarkModeTheming) + { + linkColor = cellStyle.SelectionForeColor; + } + if (PaintContentForeground(paintParts)) { if ((flags & TextFormatFlags.SingleLine) != 0) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Labels/LinkUtilities.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Labels/LinkUtilities.cs index 259e485424b..5b8db64f105 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Labels/LinkUtilities.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Labels/LinkUtilities.cs @@ -110,6 +110,21 @@ public static Color IEVisitedLinkColor } } + /// + /// Gets a light blue link color suitable for dark mode backgrounds. + /// + public static Color DarkModeLinkColor => Color.FromArgb(102, 178, 255); + + /// + /// Gets a light red active link color suitable for dark mode backgrounds. + /// + public static Color DarkModeActiveLinkColor => Color.FromArgb(255, 128, 128); + + /// + /// Gets a light purple visited link color suitable for dark mode backgrounds. + /// + public static Color DarkModeVisitedLinkColor => Color.FromArgb(200, 162, 255); + /// Produces a color for visited links using SystemColors public static Color GetVisitedLinkColor() { From dcf698fa2ba90f4b676b86e44577a2f60662fdea Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Thu, 12 Feb 2026 17:54:10 +0800 Subject: [PATCH 7/7] Declared Symbol 'System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.get' and 'set' to public API --- src/System.Windows.Forms/PublicAPI.Unshipped.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 3348c917404..3318b9388ef 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -3,6 +3,8 @@ static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Wi static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterScreen) -> System.Threading.Tasks.Task! System.Windows.Forms.ControlStyles.ApplyThemingImplicitly = 524288 -> System.Windows.Forms.ControlStyles +System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.get -> System.Drawing.Color +System.Windows.Forms.DataGridViewCellStyle.SortGlyphColor.set -> void System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler? System.Windows.Forms.Form.FormCaptionTextColorChanged -> System.EventHandler?