diff --git a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt index 3980fbc2d4e..8bd02677e21 100644 --- a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt +++ b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt @@ -221,6 +221,7 @@ GetUpdateRgn GetUserObjectInformation GetViewportExtEx GetWindow +GetWindowTheme GetWindowDpiAwarenessContext GetWindowPlacement GetWorldTransform diff --git a/src/System.Windows.Forms/System/Windows/Forms/Application.cs b/src/System.Windows.Forms/System/Windows/Forms/Application.cs index abb5ee2fb3e..f6621d51b44 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Application.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Application.cs @@ -313,6 +313,7 @@ public static void SetColorMode(SystemColorMode systemColorMode) { SystemColors.UseAlternativeColorSet = darkModeEnabled; NotifySystemEventsOfColorChange(); + PInvokeCore.EnumWindows(SendThemeChanged); } } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/System/Windows/Forms/Control.cs index 96cb4f0efcf..ea28b2f5139 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Control.cs @@ -10306,7 +10306,8 @@ 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); } @@ -10314,7 +10315,7 @@ protected virtual void SetVisibleCore(bool value) 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 @@ -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. diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeView.cs index 64203a7eb2b..aaba17a28ff 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/TreeView/TreeView.cs @@ -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... diff --git a/src/System.Windows.Forms/System/Windows/Forms/Form.cs b/src/System.Windows.Forms/System/Windows/Forms/Form.cs index f5da26c3ea1..79471b7b58a 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Form.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Form.cs @@ -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. /// + /// + /// Note: Setting to suppresses the drawing of the window border, allowing the form to have rounded corners without a visible border. Setting to resets it to the system default color. + /// /// /// The property only reflects the value that was previously set using this property. The /// event is raised accordingly when the value is @@ -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. /// + /// + /// Note: Setting to resets it to the system default color. + /// /// /// The property only reflects the value that was previously set using this property. The /// event is raised accordingly when the value is @@ -2395,6 +2401,9 @@ protected virtual void OnFormCaptionBackColorChanged(EventArgs e) /// event is raised accordingly when the value is /// changed, which allows the property to be participating in binding scenarios. /// + /// + /// Note: Setting to resets it to the system default color. + /// /// [SRCategory(nameof(SR.CatWindowStyle))] [SRDescription(nameof(SR.FormCaptionTextColorDescr))] @@ -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, @@ -4509,6 +4522,28 @@ public event DpiChangedEventHandler? DpiChanged remove => Events.RemoveHandler(s_dpiChangedEvent, value); } + /// + /// 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). + /// + /// The Windows message containing theme change information. + 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(); + } + } + /// /// Handles the WM_DPICHANGED message /// @@ -6535,11 +6570,34 @@ private void WmExitSizeMove() /// /// WM_CREATE handler /// - 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 @@ -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;