Skip to content

Dark Mode issues Fixes #13474

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ GetUpdateRgn
GetUserObjectInformation
GetViewportExtEx
GetWindow
GetWindowTheme
GetWindowDpiAwarenessContext
GetWindowPlacement
GetWorldTransform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ public static void SetColorMode(SystemColorMode systemColorMode)
{
SystemColors.UseAlternativeColorSet = darkModeEnabled;
NotifySystemEventsOfColorChange();
PInvokeCore.EnumWindows(SendThemeChanged);
}
}

Expand Down
23 changes: 21 additions & 2 deletions src/System.Windows.Forms/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10306,15 +10306,16 @@ protected virtual void SetVisibleCore(bool value)
{
// We shouldn't mess with the color mode if users haven't specifically set it.
// https://github.com/dotnet/winforms/issues/12014
if (value && Application.ColorModeSet)
// I Don't think we should be doing this for Forms here , but it is the same as the old code but execute Form Control.
if (value && Application.ColorModeSet && this is not Form)
{
PrepareDarkMode(HWND, Application.IsDarkModeEnabled);
}

PInvoke.ShowWindow(HWND, value ? ShowParams : SHOW_WINDOW_CMD.SW_HIDE);
}
}
#pragma warning restore WFO5001
#pragma warning restore WFO5001w
else if (IsHandleCreated || (value && _parent?.Created == true))
{
// We want to mark the control as visible so that CreateControl
Expand Down Expand Up @@ -12481,6 +12482,24 @@ protected virtual void WndProc(ref Message m)
case PInvokeCore.WM_SETTINGCHANGE:
if (GetExtendedState(ExtendedStates.InterestedInUserPreferenceChanged) && GetTopLevel())
{
// need Handle immediately drawing of Scrollbars.
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Application.SetColorMode(Application.ColorMode);
const string DarkModeThemeIdentifier = $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}";
PInvoke.SetWindowTheme(m.HWND,
Application.IsDarkModeEnabled
? DarkModeThemeIdentifier
: default,
null);
PInvoke.RedrawWindow(
m.HWND,
lprcUpdate: (RECT*)null,
HRGN.Null,
REDRAW_WINDOW_FLAGS.RDW_INVALIDATE
| REDRAW_WINDOW_FLAGS.RDW_FRAME
| REDRAW_WINDOW_FLAGS.RDW_ERASE
| REDRAW_WINDOW_FLAGS.RDW_ALLCHILDREN);
#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
SYSTEM_PARAMETERS_INFO_ACTION action = (SYSTEM_PARAMETERS_INFO_ACTION)(uint)m.WParamInternal;

// Left here for debugging purposes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3398,6 +3398,16 @@ protected override unsafe void WndProc(ref Message m)
case PInvokeCore.WM_SYSCOLORCHANGE:
PInvokeCore.SendMessage(this, PInvoke.TVM_SETINDENT, (WPARAM)Indent);
base.WndProc(ref m);
break;
case PInvokeCore.WM_THEMECHANGED:
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (Application.IsDarkModeEnabled)
{
HTHEME theme = PInvoke.GetWindowTheme(m.HWND);
PInvoke.CloseThemeData(theme);
m.ResultInternal = (LRESULT)0;
}

break;
case PInvokeCore.WM_SETFOCUS:
// If we get focus through the LButtonDown .. we might have done the validation...
Expand Down
65 changes: 63 additions & 2 deletions src/System.Windows.Forms/System/Windows/Forms/Form.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2265,6 +2265,9 @@ private unsafe void SetFormCornerPreferenceInternal(FormCornerPreference cornerP
/// those changes, as the Win32 API does not provide a mechanism to retrieve the current title
/// bar color.
/// </para>
/// <para>
/// Note: Setting <see cref="FormBorderColor"/> to <see cref="Color.Transparent"/> suppresses the drawing of the window border, allowing the form to have rounded corners without a visible border. Setting <see cref="FormBorderColor"/> to <see cref="Color.Empty"/> resets it to the system default color.
/// </para>
/// <para>
/// The property only reflects the value that was previously set using this property. The
/// <see cref="FormBorderColorChanged"/> event is raised accordingly when the value is
Expand Down Expand Up @@ -2327,6 +2330,9 @@ protected virtual void OnFormBorderColorChanged(EventArgs e)
/// those changes, as the Win32 API does not provide a mechanism to retrieve the current title
/// bar color.
/// </para>
/// <para>
/// Note: Setting <see cref="FormBorderColor"/> to <see cref="Color.Empty"/> resets it to the system default color.
/// </para>
/// <para>
/// The property only reflects the value that was previously set using this property. The
/// <see cref="FormCaptionBackColorChanged"/> event is raised accordingly when the value is
Expand Down Expand Up @@ -2395,6 +2401,9 @@ protected virtual void OnFormCaptionBackColorChanged(EventArgs e)
/// <see cref="FormCaptionTextColorChanged"/> event is raised accordingly when the value is
/// changed, which allows the property to be participating in binding scenarios.
/// </para>
/// <para>
/// Note: Setting <see cref="FormBorderColor"/> to <see cref="Color.Empty"/> resets it to the system default color.
/// </para>
/// </remarks>
[SRCategory(nameof(SR.CatWindowStyle))]
[SRDescription(nameof(SR.FormCaptionTextColorDescr))]
Expand Down Expand Up @@ -2453,7 +2462,11 @@ private unsafe Color GetFormAttributeColorInternal(DWMWINDOWATTRIBUTE dmwWindowA

private unsafe void SetFormAttributeColorInternal(DWMWINDOWATTRIBUTE dmwWindowAttribute, Color color)
{
COLORREF colorRef = color;
COLORREF colorRef = color.IsEmpty ? (COLORREF)(Color.White.ToArgb()) : (COLORREF)color;
if (color == Color.Transparent && dmwWindowAttribute == DWMWINDOWATTRIBUTE.DWMWA_BORDER_COLOR)
{
colorRef = (COLORREF)(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFE).ToArgb());
}

PInvoke.DwmSetWindowAttribute(
HWND,
Expand Down Expand Up @@ -4509,6 +4522,28 @@ public event DpiChangedEventHandler? DpiChanged
remove => Events.RemoveHandler(s_dpiChangedEvent, value);
}

/// <summary>
/// Handles the WM_THEMECHANGED message for the form. Updates the window's immersive dark mode attribute
/// if the application's color mode is set, ensuring the form reflects the current system theme (light or dark).
///</summary>
///<param name="m"> The Windows message containing theme change information.</param>
private unsafe void WmThemeChanged(ref Message m)
{
base.WndProc(ref m);
if (Application.ColorModeSet && GetTopLevel())
{
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
BOOL value = Application.IsDarkModeEnabled;
#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

PInvoke.DwmSetWindowAttribute(
m.HWND,
DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
&value,
(uint)sizeof(BOOL)).AssertSuccess();
}
}

/// <summary>
/// Handles the WM_DPICHANGED message
/// </summary>
Expand Down Expand Up @@ -6535,11 +6570,34 @@ private void WmExitSizeMove()
/// <summary>
/// WM_CREATE handler
/// </summary>
private void WmCreate(ref Message m)
private unsafe void WmCreate(ref Message m)
{
base.WndProc(ref m);
PInvoke.GetStartupInfo(out STARTUPINFOW si);
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (TopLevel && IsHandleCreated)
{
FormCornerPreference formCornerPreference = Properties.GetValueOrDefault(s_propFormCornerPreference, FormCornerPreference.Default);
SetFormCornerPreferenceInternal(formCornerPreference);

Color colorValue = Properties.GetValueOrDefault(s_propFormCaptionTextColor, Color.Empty);
SetFormAttributeColorInternal(DWMWINDOWATTRIBUTE.DWMWA_TEXT_COLOR, colorValue);

colorValue = Properties.GetValueOrDefault(s_propFormCaptionBackColor, Color.Empty);
SetFormAttributeColorInternal(DWMWINDOWATTRIBUTE.DWMWA_CAPTION_COLOR, colorValue);

colorValue = Properties.GetValueOrDefault(s_propFormBorderColor, Color.Empty);
SetFormAttributeColorInternal(DWMWINDOWATTRIBUTE.DWMWA_BORDER_COLOR, colorValue);
}

// Set the theme for the form. This is needed to set the dark mode theme
if (Application.IsDarkModeEnabled && Application.ColorModeSet)
{
BOOL value = true;
PInvoke.DwmSetWindowAttribute(HWND, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(int));
}

#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to chang
// If we've been created from explorer, it may
// force us to show up normal. Force our current window state to
// the specified state, unless it's _specified_ max or min
Expand Down Expand Up @@ -7116,6 +7174,9 @@ protected override void WndProc(ref Message m)
case PInvokeCore.WM_DPICHANGED:
WmDpiChanged(ref m);
break;
case PInvokeCore.WM_THEMECHANGED:
WmThemeChanged(ref m);
break;
default:
base.WndProc(ref m);
break;
Expand Down