Skip to content

Enable DataGridView dark mode theming via AppContext switch#14267

Open
LeafShi1 wants to merge 8 commits intodotnet:mainfrom
LeafShi1:Issue_14266_Add_DarkThemeExtensions
Open

Enable DataGridView dark mode theming via AppContext switch#14267
LeafShi1 wants to merge 8 commits intodotnet:mainfrom
LeafShi1:Issue_14266_Add_DarkThemeExtensions

Conversation

@LeafShi1
Copy link
Member

@LeafShi1 LeafShi1 commented Feb 4, 2026

Fixes #14266

Proposed changes

  • Introduce an AppContext quirk switch System.Windows.Forms.DataGridViewDarkModeTheming (exposed via LocalAppContextSwitches / AppContextSwitches) to control whether DataGridView uses dark-mode-specific theming, defaulting to enabled for .NET 10 and allowing apps to opt out.
  • Add ApplyDarkModeTheming in DataGridView and call it only when dark mode is active and the new switch is enabled, centralizing the dark palette (background, gridlines, header/row colors) for high contrast on dark backgrounds.
  • Update DataGridView painting logic for several cell types—core cells, checkbox cells, combo box cells, and column header cells (sort glyphs)—to use adjusted colors and rendering that remain legible and visually correct in dark mode.
  • Improve DataGridViewLinkCell in dark mode by using shared LinkUtilities link colors (aligned with LinkLabel’s dark colors) and ensuring selected links render with appropriate selection foreground color for sufficient contrast.
  • Keep existing light-mode/legacy behavior unchanged when the AppContext switch is disabled, so existing applications can retain their current appearance without code changes.

Customer Impact

  • Users get a DataGridView that automatically renders with readable, high-contrast colors in dark mode by default, while app developers can still opt out via an AppContext switch to preserve existing light-themed behavior if needed.

Regression?

  • No

Risk

  • Minimal

Screenshots

Before

image

After

image

Test methodology

  • Manually

Test environment(s)

  • 11.0.0-preview.2.26080.101
Microsoft Reviewers: Open in CodeFlow

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

❌ Patch coverage is 4.61538% with 248 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.16791%. Comparing base (9b546b7) to head (dcf698f).
⚠️ Report is 11 commits behind head on main.

Additional details and impacted files
@@                 Coverage Diff                 @@
##                main      #14267         +/-   ##
===================================================
- Coverage   77.18395%   77.16791%   -0.01604%     
===================================================
  Files           3279        3279                 
  Lines         645138      645263        +125     
  Branches       47730       47755         +25     
===================================================
- Hits          497943      497936          -7     
- Misses        143503      143631        +128     
- Partials        3692        3696          +4     
Flag Coverage Δ
Debug 77.16791% <4.61538%> (-0.01604%) ⬇️
integration 18.98655% <1.92308%> (-0.00409%) ⬇️
production 52.03815% <4.61538%> (-0.02504%) ⬇️
test 97.40479% <ø> (ø)
unit 49.49919% <4.61538%> (-0.00344%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LeafShi1 LeafShi1 changed the title Add theming API and improve selection/grid readability Enable DataGridView dark mode theming via AppContext switch Feb 12, 2026
@LeafShi1 LeafShi1 marked this pull request as ready for review February 12, 2026 09:54
@LeafShi1 LeafShi1 requested a review from a team as a code owner February 12, 2026 09:54
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces dark mode theming for the DataGridView control via an AppContext switch (System.Windows.Forms.DataGridViewDarkModeTheming), defaulting to enabled for .NET 10+. The changes provide better visual appearance and contrast in dark mode while preserving opt-out capability for backward compatibility.

Changes:

  • Added AppContext switch to control DataGridView dark mode theming, with inheritance through style system
  • Implemented dark mode rendering for multiple cell types including link cells, combo box cells, checkbox cells, and column header sort glyphs
  • Added shared dark mode link colors to LinkUtilities for consistent link rendering across the DataGridView

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 31 comments.

Show a summary per file
File Description
src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs Added DataGridViewDarkModeTheming switch with .NET 10+ default
src/System.Windows.Forms/System/Windows/Forms/Controls/Labels/LinkUtilities.cs Added dark mode color constants for links
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs Implemented dark mode link colors with proper selection handling
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewComboBoxCell.DataGridViewComboBoxCellRenderer.cs Added dark mode rendering for combo box dropdown and readonly buttons
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewColumnHeaderCell.cs Refactored sort glyph rendering to support solid white glyphs in dark mode
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCheckBoxCell.cs Switched to CheckBoxRenderer.DrawCheckBoxWithVisualStyles for dark mode support
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCellStyle.cs Added SortGlyphColor property to allow customization of sort arrow color
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridViewCell.cs Enhanced GetContrastedColors to provide better contrast in dark mode
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs Added ApplyDarkModeTheming method and integration in OnHandleCreated
src/System.Windows.Forms/PublicAPI.Unshipped.txt Documented new SortGlyphColor public API

Comment on lines +85 to +90
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);
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded Color.FromArgb(45, 45, 45) and Color.FromArgb(100, 100, 100) magic numbers should be replaced with named constants. These same values appear in ToolStripSystemDarkModeRenderer. Consider extracting these common dark mode colors to a shared location (such as a DarkModeColors class) to ensure consistency across controls and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +1008 to +1012
// 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;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SortGlyphColor property from cellStyle is inherited in BuildInheritedColumnHeaderCellStyle but is never actually used in the painting logic. The sort glyph rendering code hardcodes Color.White for dark mode on line 1011-1012 instead of checking if cellStyle.SortGlyphColor is set. The code should check cellStyle.SortGlyphColor first and only fall back to Color.White if it's Color.Empty, allowing users to customize the sort glyph color.

Suggested change
// 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;
// In Dark Mode with quirk enabled, prefer SortGlyphColor when set, otherwise use solid white glyph for better visibility
if (Application.IsDarkModeEnabled && AppContextSwitches.DataGridViewDarkModeTheming)
{
Color sortGlyphColor = cellStyle.SortGlyphColor;
if (sortGlyphColor == Color.Empty)
{
sortGlyphColor = Color.White;
}
darkColor = sortGlyphColor;
lightColor = sortGlyphColor;

Copilot uses AI. Check for mistakes.
Color onSurface = SystemColors.WindowText;
Color headerBg = SystemColors.ControlDarkDark;
Color headerFg = SystemColors.ActiveCaptionText;
Color selectionBg = Color.FromArgb(0x33, 0x66, 0xCC);
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic numbers Color.FromArgb(0x33, 0x66, 0xCC) for selectionBg should be replaced with a named constant to improve code clarity and maintainability. Consider defining this as a constant like DarkModeSelectionBackground.

Copilot uses AI. Check for mistakes.
Comment on lines +1438 to +1449
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;
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetContrastedColors method now checks Application.IsDarkModeEnabled and returns dark-mode-optimized colors for all DataGridView cells, but it does not verify the AppContextSwitches.DataGridViewDarkModeTheming switch. This means border rendering and other uses of GetContrastedColors will apply dark mode logic even if the user has opted out via the AppContext switch. If the intent is that opting out of the quirk should preserve all existing light-mode behavior, then GetContrastedColors should also check the AppContext switch. Alternatively, if general dark mode color improvements are intended to apply regardless of the quirk, this should be clarified in the PR description.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +66
if (Application.IsDarkModeEnabled)
{
InitializeRenderer(ComboBoxDropDownButtonElement, (int)state);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DataGridViewComboBoxCellRenderer methods DrawDropDownButton and DrawReadOnlyButton check Application.IsDarkModeEnabled but do not verify the AppContextSwitches.DataGridViewDarkModeTheming switch. This means dark mode rendering will be applied even if the user has opted out via the AppContext switch. Both dark mode conditions should check both Application.IsDarkModeEnabled AND AppContextSwitches.DataGridViewDarkModeTheming to respect the opt-out mechanism.

Copilot uses AI. Check for mistakes.
g.DrawLine(penControlDark,
sortGlyphLocation.X,
sortGlyphLocation.Y + 1,
sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1,
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible loss of precision: any fraction will be lost.

Copilot uses AI. Check for mistakes.
g.DrawLine(penControlDark,
sortGlyphLocation.X + 1,
sortGlyphLocation.Y + 1,
sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1,
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible loss of precision: any fraction will be lost.

Copilot uses AI. Check for mistakes.
g.DrawLine(penControlLightLight,
sortGlyphLocation.X,
sortGlyphLocation.Y + 1,
sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1,
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible loss of precision: any fraction will be lost.

Copilot uses AI. Check for mistakes.
g.DrawLine(penControlLightLight,
sortGlyphLocation.X + 1,
sortGlyphLocation.Y + 1,
sortGlyphLocation.X + s_sortGlyphWidth / 2 - 1,
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible loss of precision: any fraction will be lost.

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +147
if (framework.Version.Major >= 10)
{
// Behavior changes added in .NET 10

if (switchName == DataGridViewDarkModeThemingSwitchName)
{
return true;
}
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 'if' statements can be combined.

Copilot uses AI. Check for mistakes.
@dotnet-policy-service dotnet-policy-service bot removed the draft draft PR label Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Dark Mode] DataGridView Dark Mode default colors look inconsistent and visually unbalanced

1 participant