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 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?
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..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)
@@ -2853,6 +2895,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)
@@ -15242,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/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/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/DataGridViewColumnHeaderCell.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs
index 047158c8631..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
@@ -1001,175 +1001,202 @@ private Rectangle PaintPrivate(
if (paint && displaySortGlyph && PaintContentBackground(paintParts))
{
- (Color darkColor, Color lightColor) = GetContrastedColors(cellStyle.BackColor);
+ Color darkColor;
+ Color lightColor;
+ bool useFlatStyle = false;
+
+ // In Dark Mode with quirk enabled, use a solid white glyph for better visibility
+ if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming)
+ {
+ darkColor = Color.White;
+ lightColor = Color.White;
+ 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++)
- {
- 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 + 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);
- 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);
+ }
+ else
+ {
+ switch (advancedBorderStyle.Right)
+ {
+ 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);
+ g.DrawLine(penControlDark,
+ sortGlyphLocation.X,
+ sortGlyphLocation.Y + s_sortGlyphHeight - 1,
+ sortGlyphLocation.X + s_sortGlyphWidth - 2,
+ sortGlyphLocation.Y + s_sortGlyphHeight - 1);
+ 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
+ {
+ 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;
+ }
}
}
}
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);
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()
{