From 8b345fe8edec7ea747e43154a2639b8cbdb20032 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 3 Nov 2024 20:08:13 -0800 Subject: [PATCH 01/18] Disabling ToolTip visual styles to make it dark enough in Dark mode --- .../System/Windows/Forms/ToolTip/ToolTip.cs | 19 +++++++----- .../System/Windows/Forms/ToolTipTests.cs | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index 6fad5f45a92..f99656f89aa 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -718,6 +718,13 @@ private unsafe void CreateHandle() { PInvoke.SetWindowTheme(HWND, string.Empty, string.Empty); } + +#pragma warning disable WFO5001 + if (Application.IsDarkModeEnabled) + { + PInvoke.SetWindowTheme(HWND, string.Empty, string.Empty); + } +#pragma warning restore WFO5001 } // If in OwnerDraw mode, we don't want the default border. @@ -770,15 +777,11 @@ private unsafe void CreateHandle() // Set active status. PInvokeCore.SendMessage(this, PInvoke.TTM_ACTIVATE, (WPARAM)(BOOL)_active); - if (BackColor != SystemColors.Info) - { - PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)BackColor); - } + // Set background color. + PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)_backColor); - if (ForeColor != SystemColors.InfoText) - { - PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)ForeColor); - } + // Set text color. + PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)_foreColor); if (_toolTipIcon > 0 || !string.IsNullOrEmpty(_toolTipTitle)) { diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index bbfd4f46ae7..16c0d18a062 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -782,6 +782,36 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() Assert.Equal("System.Windows.Forms.ToolTip InitialDelay: 500, ShowAlways: False", toolTip.ToString()); } +#pragma warning disable WFO5001 + [WinFormsFact] + public void ToolTip_DarkMode_GetColors_ReturnsExpected() + { + if (SystemInformation.HighContrast) + { + // We don't run this test in HighContrast mode. + return; + } + + SystemColorMode colorMode = Application.ColorMode; + Application.SetColorMode(SystemColorMode.Dark); + + using SubToolTip toolTip = new(); + + Assert.NotEqual(IntPtr.Zero, toolTip.Handle); // A workaround to create the toolTip native window Handle + + var backgroundColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPBKCOLOR); + Color backColor = ColorTranslator.FromWin32((int)backgroundColor); + + var textColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPTEXTCOLOR); + Color foreColor = ColorTranslator.FromWin32((int)textColor); + + Assert.Equal(SystemColors.Info.ToArgb(), backColor.ToArgb()); + Assert.Equal(SystemColors.InfoText.ToArgb(), foreColor.ToArgb()); + + Application.SetColorMode(colorMode); + } +#pragma warning restore WFO5001 + [WinFormsFact] public void ToolTip_SetToolTipToControl_Invokes_SetToolTip_OfControl() { From f5a017b92090ba392fa18fd5ba280ba6df075282 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 5 Nov 2024 20:37:47 -0800 Subject: [PATCH 02/18] Feedback from code review --- .../System/Windows/Forms/ToolTip/ToolTip.cs | 7 +-- .../ApplicationColorModeScope.cs | 44 +++++++++++++++++++ .../System/Windows/Forms/ToolTipTests.cs | 10 ++--- 3 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index f99656f89aa..ced2f6b464b 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -714,13 +714,8 @@ private unsafe void CreateHandle() _window.CreateHandle(cp); - if (SystemInformation.HighContrast) - { - PInvoke.SetWindowTheme(HWND, string.Empty, string.Empty); - } - #pragma warning disable WFO5001 - if (Application.IsDarkModeEnabled) + if (SystemInformation.HighContrast || Application.IsDarkModeEnabled) { PInvoke.SetWindowTheme(HWND, string.Empty, string.Empty); } diff --git a/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs b/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs new file mode 100644 index 00000000000..7bdd79a1d67 --- /dev/null +++ b/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Forms; + +namespace System; + +#pragma warning disable WFO5001 +/// +/// Scope for setting the default color mode (dark mode) for the application. Use in a statement. +/// +public readonly ref struct ApplicationColorModeScope +{ + private readonly SystemColorMode _originalColorMode; + + public ApplicationColorModeScope(SystemColorMode colorMode) + { + // Prevent multiple ApplicationColorModeScopes from running simultaneously. + // Using Monitor to allow recursion on the same thread. + Monitor.Enter(typeof(ApplicationColorModeScope)); + _originalColorMode = Application.ColorMode; + + if (_originalColorMode != colorMode) + { + Application.SetColorMode(colorMode); + } + } + + public void Dispose() + { + try + { + if (Application.ColorMode != _originalColorMode) + { + Application.SetColorMode(_originalColorMode); + } + } + finally + { + Monitor.Exit(typeof(ApplicationColorModeScope)); + } + } +} +#pragma warning restore WFO5001 diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 16c0d18a062..b1704d52b9a 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -782,7 +782,7 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() Assert.Equal("System.Windows.Forms.ToolTip InitialDelay: 500, ShowAlways: False", toolTip.ToString()); } -#pragma warning disable WFO5001 + #pragma warning disable WFO5001 [WinFormsFact] public void ToolTip_DarkMode_GetColors_ReturnsExpected() { @@ -792,12 +792,10 @@ public void ToolTip_DarkMode_GetColors_ReturnsExpected() return; } - SystemColorMode colorMode = Application.ColorMode; - Application.SetColorMode(SystemColorMode.Dark); - + using ApplicationColorModeScope colorModeScope = new(colorMode: SystemColorMode.Dark); using SubToolTip toolTip = new(); - Assert.NotEqual(IntPtr.Zero, toolTip.Handle); // A workaround to create the toolTip native window Handle + toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle var backgroundColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPBKCOLOR); Color backColor = ColorTranslator.FromWin32((int)backgroundColor); @@ -807,8 +805,6 @@ public void ToolTip_DarkMode_GetColors_ReturnsExpected() Assert.Equal(SystemColors.Info.ToArgb(), backColor.ToArgb()); Assert.Equal(SystemColors.InfoText.ToArgb(), foreColor.ToArgb()); - - Application.SetColorMode(colorMode); } #pragma warning restore WFO5001 From e980d698d5a5767326f3605199e83b961bb095ef Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 5 Nov 2024 20:39:57 -0800 Subject: [PATCH 03/18] Fix spacing --- .../tests/UnitTests/System/Windows/Forms/ToolTipTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index b1704d52b9a..91116bd9f7b 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -782,7 +782,7 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() Assert.Equal("System.Windows.Forms.ToolTip InitialDelay: 500, ShowAlways: False", toolTip.ToString()); } - #pragma warning disable WFO5001 +#pragma warning disable WFO5001 [WinFormsFact] public void ToolTip_DarkMode_GetColors_ReturnsExpected() { From 6e1fd22091d936484dcce2f1bfca6fc7de6d8664 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 5 Nov 2024 21:17:46 -0800 Subject: [PATCH 04/18] More fluent assertions --- .../tests/UnitTests/System/Windows/Forms/ToolTipTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 91116bd9f7b..2be455f84c2 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -803,8 +803,8 @@ public void ToolTip_DarkMode_GetColors_ReturnsExpected() var textColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPTEXTCOLOR); Color foreColor = ColorTranslator.FromWin32((int)textColor); - Assert.Equal(SystemColors.Info.ToArgb(), backColor.ToArgb()); - Assert.Equal(SystemColors.InfoText.ToArgb(), foreColor.ToArgb()); + backColor.ToArgb().Should().Be(SystemColors.Info.ToArgb()); + foreColor.ToArgb().Should().Be(SystemColors.InfoText.ToArgb()); } #pragma warning restore WFO5001 From ae2c320499f64e2ae84673ca8303256ccbacd89e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 6 Nov 2024 19:22:07 -0800 Subject: [PATCH 05/18] ApplicationColorModeScope feedback from code review --- .../TestUtilities/ApplicationColorModeScope.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs b/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs index 7bdd79a1d67..47b8eed443d 100644 --- a/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs +++ b/src/System.Windows.Forms/tests/TestUtilities/ApplicationColorModeScope.cs @@ -15,9 +15,6 @@ public readonly ref struct ApplicationColorModeScope public ApplicationColorModeScope(SystemColorMode colorMode) { - // Prevent multiple ApplicationColorModeScopes from running simultaneously. - // Using Monitor to allow recursion on the same thread. - Monitor.Enter(typeof(ApplicationColorModeScope)); _originalColorMode = Application.ColorMode; if (_originalColorMode != colorMode) @@ -28,16 +25,9 @@ public ApplicationColorModeScope(SystemColorMode colorMode) public void Dispose() { - try + if (Application.ColorMode != _originalColorMode) { - if (Application.ColorMode != _originalColorMode) - { - Application.SetColorMode(_originalColorMode); - } - } - finally - { - Monitor.Exit(typeof(ApplicationColorModeScope)); + Application.SetColorMode(_originalColorMode); } } } From 3278d8d11df23b8623d41c4fb69c51911ade7930 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 6 Nov 2024 20:53:34 -0800 Subject: [PATCH 06/18] Set theme to DarkMode_Explorer and round the corners with a small radius --- .../src/System/Windows/Forms/ToolTip/ToolTip.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index ced2f6b464b..7b2d90bd72e 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Drawing; +using Windows.Win32.Graphics.Dwm; namespace System.Windows.Forms; @@ -20,6 +21,9 @@ public partial class ToolTip : Component, IExtenderProvider, IHandle // These values are initialized using the default double-click time value. internal const int DefaultDelay = 500; + internal const string DarkModeIdentifier = "DarkMode"; + internal const string ExplorerThemeIdentifier = "Explorer"; + // These values are copied from the ComCtl32's tooltip. private const int ReshowRatio = 5; private const int AutoPopRatio = 10; @@ -715,10 +719,21 @@ private unsafe void CreateHandle() _window.CreateHandle(cp); #pragma warning disable WFO5001 - if (SystemInformation.HighContrast || Application.IsDarkModeEnabled) + if (SystemInformation.HighContrast) { PInvoke.SetWindowTheme(HWND, string.Empty, string.Empty); } + else if (Application.IsDarkModeEnabled) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + HWND, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + + PInvokeCore.SendMessage(HWND, PInvoke.TTM_SETWINDOWTHEME, default, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}"); + } #pragma warning restore WFO5001 } From f4d6102c60c2f3d08a7b600f576432bb64914b09 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 6 Nov 2024 22:11:29 -0800 Subject: [PATCH 07/18] Assert the rounded corner preference --- .../UnitTests/System/Windows/Forms/ToolTipTests.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 2be455f84c2..5e825a82559 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -6,6 +6,7 @@ using System.Windows.Forms.Automation; using Moq; using Moq.Protected; +using Windows.Win32.Graphics.Dwm; namespace System.Windows.Forms.Tests; @@ -784,7 +785,7 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() #pragma warning disable WFO5001 [WinFormsFact] - public void ToolTip_DarkMode_GetColors_ReturnsExpected() + public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() { if (SystemInformation.HighContrast) { @@ -803,8 +804,17 @@ public void ToolTip_DarkMode_GetColors_ReturnsExpected() var textColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPTEXTCOLOR); Color foreColor = ColorTranslator.FromWin32((int)textColor); + DWMWINDOWATTRIBUTE dmwWindowAttribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE; + DWM_WINDOW_CORNER_PREFERENCE cornerPreference; + PInvoke.DwmGetWindowAttribute( + toolTip.HWND, + dmwWindowAttribute, + &cornerPreference, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + backColor.ToArgb().Should().Be(SystemColors.Info.ToArgb()); foreColor.ToArgb().Should().Be(SystemColors.InfoText.ToArgb()); + cornerPreference.Should().Be(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL); } #pragma warning restore WFO5001 From a4ead96ece9d9842dd8c3d290bb8d675e070cee4 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 6 Nov 2024 23:12:49 -0800 Subject: [PATCH 08/18] Corner preference is supported starting with Windows 11 Build 22000 --- .../System/Windows/Forms/ToolTip/ToolTip.cs | 15 ++++++----- .../System/Windows/Forms/ToolTipTests.cs | 27 ++++++++++++++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index 7b2d90bd72e..e62a266020c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -725,12 +725,15 @@ private unsafe void CreateHandle() } else if (Application.IsDarkModeEnabled) { - DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; - PInvoke.DwmSetWindowAttribute( - HWND, - DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, - &roundSmall, - sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + if (OsVersion.IsWindows11_OrGreater()) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + HWND, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } PInvokeCore.SendMessage(HWND, PInvoke.TTM_SETWINDOWTHEME, default, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}"); } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 5e825a82559..03bf1665e9b 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -785,7 +785,7 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() #pragma warning disable WFO5001 [WinFormsFact] - public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() + public void ToolTip_DarkMode_GetColors_ReturnsExpected() { if (SystemInformation.HighContrast) { @@ -804,6 +804,29 @@ public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() var textColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPTEXTCOLOR); Color foreColor = ColorTranslator.FromWin32((int)textColor); + backColor.ToArgb().Should().Be(SystemColors.Info.ToArgb()); + foreColor.ToArgb().Should().Be(SystemColors.InfoText.ToArgb()); + } + + [WinFormsFact] + public unsafe void ToolTip_DarkMode_GetCornerPreference_ReturnsExpected() + { + if (!OsVersion.IsWindows11_OrGreater()) + { + return; + } + + if (SystemInformation.HighContrast) + { + // We don't run this test in HighContrast mode. + return; + } + + using ApplicationColorModeScope colorModeScope = new(colorMode: SystemColorMode.Dark); + using SubToolTip toolTip = new(); + + toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle + DWMWINDOWATTRIBUTE dmwWindowAttribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE; DWM_WINDOW_CORNER_PREFERENCE cornerPreference; PInvoke.DwmGetWindowAttribute( @@ -812,8 +835,6 @@ public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() &cornerPreference, sizeof(DWM_WINDOW_CORNER_PREFERENCE)); - backColor.ToArgb().Should().Be(SystemColors.Info.ToArgb()); - foreColor.ToArgb().Should().Be(SystemColors.InfoText.ToArgb()); cornerPreference.Should().Be(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL); } #pragma warning restore WFO5001 From c34aa2df9eb62868656d3895538821d861e4d038 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 7 Nov 2024 20:35:08 -0800 Subject: [PATCH 09/18] Use default corner preference for balloon windows --- .../System/Windows/Forms/ToolTip/ToolTip.cs | 14 +++++---- .../System/Windows/Forms/ToolTipTests.cs | 30 +++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index e62a266020c..dbda709998d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -725,7 +725,7 @@ private unsafe void CreateHandle() } else if (Application.IsDarkModeEnabled) { - if (OsVersion.IsWindows11_OrGreater()) + if (!_isBalloon && OsVersion.IsWindows11_OrGreater()) { DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; PInvoke.DwmSetWindowAttribute( @@ -790,11 +790,15 @@ private unsafe void CreateHandle() // Set active status. PInvokeCore.SendMessage(this, PInvoke.TTM_ACTIVATE, (WPARAM)(BOOL)_active); - // Set background color. - PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)_backColor); + if (BackColor != SystemColors.Info) + { + PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)BackColor); + } - // Set text color. - PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)_foreColor); + if (ForeColor != SystemColors.InfoText) + { + PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)ForeColor); + } if (_toolTipIcon > 0 || !string.IsNullOrEmpty(_toolTipTitle)) { diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 03bf1665e9b..1460c69b446 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -785,7 +785,7 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() #pragma warning disable WFO5001 [WinFormsFact] - public void ToolTip_DarkMode_GetColors_ReturnsExpected() + public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() { if (SystemInformation.HighContrast) { @@ -798,18 +798,13 @@ public void ToolTip_DarkMode_GetColors_ReturnsExpected() toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle - var backgroundColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPBKCOLOR); - Color backColor = ColorTranslator.FromWin32((int)backgroundColor); - - var textColor = PInvokeCore.SendMessage(toolTip, PInvoke.TTM_GETTIPTEXTCOLOR); - Color foreColor = ColorTranslator.FromWin32((int)textColor); - - backColor.ToArgb().Should().Be(SystemColors.Info.ToArgb()); - foreColor.ToArgb().Should().Be(SystemColors.InfoText.ToArgb()); + toolTip.BackColor.Should().Be(SystemColors.Info); + toolTip.ForeColor.Should().Be(SystemColors.InfoText); } - [WinFormsFact] - public unsafe void ToolTip_DarkMode_GetCornerPreference_ReturnsExpected() + [WinFormsTheory] + [BoolData] + public unsafe void ToolTip_DarkMode_GetCornerPreference_ReturnsExpected(bool value) { if (!OsVersion.IsWindows11_OrGreater()) { @@ -823,19 +818,24 @@ public unsafe void ToolTip_DarkMode_GetCornerPreference_ReturnsExpected() } using ApplicationColorModeScope colorModeScope = new(colorMode: SystemColorMode.Dark); - using SubToolTip toolTip = new(); + using SubToolTip toolTip = new() + { + IsBalloon = value, + }; toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle - DWMWINDOWATTRIBUTE dmwWindowAttribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE; DWM_WINDOW_CORNER_PREFERENCE cornerPreference; PInvoke.DwmGetWindowAttribute( toolTip.HWND, - dmwWindowAttribute, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(DWM_WINDOW_CORNER_PREFERENCE)); - cornerPreference.Should().Be(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL); + cornerPreference.Should().Be( + toolTip.IsBalloon + ? DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DEFAULT + : DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL); } #pragma warning restore WFO5001 From 10e0bbb935d3a8f82cad9e0be01dd5b9e296da1e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sat, 9 Nov 2024 14:54:05 -0800 Subject: [PATCH 10/18] Initialize colors from dark mode explorer tooltip theme subclass --- .../System/Windows/Forms/ToolTip/ToolTip.cs | 21 ++++++++++++++++--- .../System/Windows/Forms/ToolTipTests.cs | 16 ++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index dbda709998d..1d6f3d7b8e5 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Drawing; +using System.Windows.Forms.VisualStyles; using Windows.Win32.Graphics.Dwm; namespace System.Windows.Forms; @@ -21,8 +22,7 @@ public partial class ToolTip : Component, IExtenderProvider, IHandle // These values are initialized using the default double-click time value. internal const int DefaultDelay = 500; - internal const string DarkModeIdentifier = "DarkMode"; - internal const string ExplorerThemeIdentifier = "Explorer"; + internal const string TooltipThemeSubclassIdentifier = "Tooltip"; // These values are copied from the ComCtl32's tooltip. private const int ReshowRatio = 5; @@ -735,7 +735,22 @@ private unsafe void CreateHandle() sizeof(DWM_WINDOW_CORNER_PREFERENCE)); } - PInvokeCore.SendMessage(HWND, PInvoke.TTM_SETWINDOWTHEME, default, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}"); + PInvokeCore.SendMessage( + HWND, + PInvoke.TTM_SETWINDOWTHEME, + default, + $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}"); + + var renderer = new VisualStyleRenderer( + $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}::{TooltipThemeSubclassIdentifier}", + 0, + 0); + + _backColor = renderer.GetColor(ColorProperty.FillColor); + _foreColor = renderer.GetColor(ColorProperty.TextColor); + + PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)_backColor); + PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)_foreColor); } #pragma warning restore WFO5001 } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 1460c69b446..f113b2b6a86 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Drawing; using System.Windows.Forms.Automation; +using System.Windows.Forms.VisualStyles; using Moq; using Moq.Protected; using Windows.Win32.Graphics.Dwm; @@ -793,13 +794,24 @@ public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() return; } + var renderer = new VisualStyleRenderer( + $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}::{ToolTip.TooltipThemeSubclassIdentifier}", + 0, + 0); + var fillColor = renderer.GetColor(ColorProperty.FillColor); + var textColor = renderer.GetColor(ColorProperty.TextColor); using ApplicationColorModeScope colorModeScope = new(colorMode: SystemColorMode.Dark); using SubToolTip toolTip = new(); toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle - toolTip.BackColor.Should().Be(SystemColors.Info); - toolTip.ForeColor.Should().Be(SystemColors.InfoText); + Color backColor = ColorTranslator.FromWin32((int)PInvokeCore.SendMessage(toolTip.HWND, PInvoke.TTM_GETTIPBKCOLOR)); + Color foreColor = ColorTranslator.FromWin32((int)PInvokeCore.SendMessage(toolTip.HWND, PInvoke.TTM_GETTIPTEXTCOLOR)); + + backColor.Should().Be(fillColor); + foreColor.Should().Be(textColor); + toolTip.BackColor.Should().Be(fillColor); + toolTip.ForeColor.Should().Be(textColor); } [WinFormsTheory] From d4c985be2496f6773f73ce898856cd1836fe71bb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sat, 9 Nov 2024 19:10:46 -0800 Subject: [PATCH 11/18] Set colors in a similar way as ListView and TreeView --- .../src/System/Windows/Forms/ToolTip/ToolTip.cs | 9 ++++----- .../tests/UnitTests/System/Windows/Forms/ToolTipTests.cs | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index 1d6f3d7b8e5..8a6d2c9efbb 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -748,9 +748,6 @@ private unsafe void CreateHandle() _backColor = renderer.GetColor(ColorProperty.FillColor); _foreColor = renderer.GetColor(ColorProperty.TextColor); - - PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)_backColor); - PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)_foreColor); } #pragma warning restore WFO5001 } @@ -805,15 +802,17 @@ private unsafe void CreateHandle() // Set active status. PInvokeCore.SendMessage(this, PInvoke.TTM_ACTIVATE, (WPARAM)(BOOL)_active); - if (BackColor != SystemColors.Info) +#pragma warning disable WFO5001 + if (BackColor != SystemColors.Info || Application.IsDarkModeEnabled) { PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)BackColor); } - if (ForeColor != SystemColors.InfoText) + if (ForeColor != SystemColors.InfoText || Application.IsDarkModeEnabled) { PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)ForeColor); } +#pragma warning restore WFO5001 if (_toolTipIcon > 0 || !string.IsNullOrEmpty(_toolTipTitle)) { diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index f113b2b6a86..346b176368e 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -800,6 +800,7 @@ public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() 0); var fillColor = renderer.GetColor(ColorProperty.FillColor); var textColor = renderer.GetColor(ColorProperty.TextColor); + using ApplicationColorModeScope colorModeScope = new(colorMode: SystemColorMode.Dark); using SubToolTip toolTip = new(); From 674c1c736e6fc04f8d1eede8b01e6f2303d0ee08 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 10 Nov 2024 21:35:07 -0800 Subject: [PATCH 12/18] Utilize theme for visual styles and remove the get colors test --- .../System/Windows/Forms/ToolTip/ToolTip.cs | 23 ++++---------- .../System/Windows/Forms/ToolTipTests.cs | 31 ------------------- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index 8a6d2c9efbb..3013241633c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -3,7 +3,6 @@ using System.ComponentModel; using System.Drawing; -using System.Windows.Forms.VisualStyles; using Windows.Win32.Graphics.Dwm; namespace System.Windows.Forms; @@ -736,18 +735,10 @@ private unsafe void CreateHandle() } PInvokeCore.SendMessage( - HWND, - PInvoke.TTM_SETWINDOWTHEME, - default, - $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}"); - - var renderer = new VisualStyleRenderer( - $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}::{TooltipThemeSubclassIdentifier}", - 0, - 0); - - _backColor = renderer.GetColor(ColorProperty.FillColor); - _foreColor = renderer.GetColor(ColorProperty.TextColor); + HWND, + PInvoke.TTM_SETWINDOWTHEME, + default, + $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}"); } #pragma warning restore WFO5001 } @@ -802,17 +793,15 @@ private unsafe void CreateHandle() // Set active status. PInvokeCore.SendMessage(this, PInvoke.TTM_ACTIVATE, (WPARAM)(BOOL)_active); -#pragma warning disable WFO5001 - if (BackColor != SystemColors.Info || Application.IsDarkModeEnabled) + if (BackColor != SystemColors.Info) { PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPBKCOLOR, (WPARAM)BackColor); } - if (ForeColor != SystemColors.InfoText || Application.IsDarkModeEnabled) + if (ForeColor != SystemColors.InfoText) { PInvokeCore.SendMessage(this, PInvoke.TTM_SETTIPTEXTCOLOR, (WPARAM)ForeColor); } -#pragma warning restore WFO5001 if (_toolTipIcon > 0 || !string.IsNullOrEmpty(_toolTipTitle)) { diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs index 346b176368e..f93e3786d55 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ToolTipTests.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using System.Drawing; using System.Windows.Forms.Automation; -using System.Windows.Forms.VisualStyles; using Moq; using Moq.Protected; using Windows.Win32.Graphics.Dwm; @@ -785,36 +784,6 @@ public void ToolTip_ToString_Invoke_ReturnsExpected() } #pragma warning disable WFO5001 - [WinFormsFact] - public unsafe void ToolTip_DarkMode_GetColors_ReturnsExpected() - { - if (SystemInformation.HighContrast) - { - // We don't run this test in HighContrast mode. - return; - } - - var renderer = new VisualStyleRenderer( - $"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}::{ToolTip.TooltipThemeSubclassIdentifier}", - 0, - 0); - var fillColor = renderer.GetColor(ColorProperty.FillColor); - var textColor = renderer.GetColor(ColorProperty.TextColor); - - using ApplicationColorModeScope colorModeScope = new(colorMode: SystemColorMode.Dark); - using SubToolTip toolTip = new(); - - toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle - - Color backColor = ColorTranslator.FromWin32((int)PInvokeCore.SendMessage(toolTip.HWND, PInvoke.TTM_GETTIPBKCOLOR)); - Color foreColor = ColorTranslator.FromWin32((int)PInvokeCore.SendMessage(toolTip.HWND, PInvoke.TTM_GETTIPTEXTCOLOR)); - - backColor.Should().Be(fillColor); - foreColor.Should().Be(textColor); - toolTip.BackColor.Should().Be(fillColor); - toolTip.ForeColor.Should().Be(textColor); - } - [WinFormsTheory] [BoolData] public unsafe void ToolTip_DarkMode_GetCornerPreference_ReturnsExpected(bool value) From 26e4f93d539a9720353b152182e7b6a33147ed17 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 10 Nov 2024 21:40:12 -0800 Subject: [PATCH 13/18] Remove constant --- .../src/System/Windows/Forms/ToolTip/ToolTip.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs index 3013241633c..efcfa1c6c4f 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ToolTip/ToolTip.cs @@ -21,8 +21,6 @@ public partial class ToolTip : Component, IExtenderProvider, IHandle // These values are initialized using the default double-click time value. internal const int DefaultDelay = 500; - internal const string TooltipThemeSubclassIdentifier = "Tooltip"; - // These values are copied from the ComCtl32's tooltip. private const int ReshowRatio = 5; private const int AutoPopRatio = 10; From 05412b8ee35a156db874bded0fec5f9344e59dc1 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Nov 2024 19:45:15 -0800 Subject: [PATCH 14/18] Darken the ListView ToolTip in Dark mode --- .../Forms/Controls/ListView/ListView.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs index 24a6688e4d7..630db12779d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using System.Windows.Forms.Layout; using System.Windows.Forms.VisualStyles; +using Windows.Win32.Graphics.Dwm; using Windows.Win32.System.Variant; using Windows.Win32.UI.Accessibility; using Windows.Win32.UI.Input.KeyboardAndMouse; @@ -4512,7 +4513,7 @@ protected override void OnFontChanged(EventArgs e) InvalidateColumnHeaders(); } - protected override void OnHandleCreated(EventArgs e) + protected override unsafe void OnHandleCreated(EventArgs e) { // don't persist flipViewToLargeIconAndSmallIcon across handle recreations... FlipViewToLargeIconAndSmallIcon = false; @@ -4533,6 +4534,21 @@ protected override void OnHandleCreated(EventArgs e) // Get the ListView's ColumnHeader handle: HWND columnHeaderHandle = (HWND)PInvokeCore.SendMessage(this, PInvoke.LVM_GETHEADER, (WPARAM)0, (LPARAM)0); PInvoke.SetWindowTheme(columnHeaderHandle, $"{DarkModeIdentifier}_{ItemsViewThemeIdentifier}", null); + + // Get the ListView's ToolTip handle: + HWND toolTipHandle = (HWND)PInvokeCore.SendMessage(this, PInvoke.LVM_GETTOOLTIPS, (WPARAM)0, (LPARAM)0); + PInvoke.SetWindowTheme(toolTipHandle, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + + // Round the corners of the ToolTip window. + if (OsVersion.IsWindows11_OrGreater()) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + toolTipHandle, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } } #pragma warning restore WFO5001 From 1e35f2ba312a69493163929007f6373624094dfe Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Nov 2024 20:26:38 -0800 Subject: [PATCH 15/18] Darken the TreeView ToolTip in Dark mode --- .../Forms/Controls/TreeView/TreeView.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs index a7e66b35eb7..f26fdde2757 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Windows.Forms.Layout; using System.Windows.Forms.VisualStyles; +using Windows.Win32.Graphics.Dwm; using Windows.Win32.System.Variant; using Windows.Win32.UI.Accessibility; using static System.Windows.Forms.TreeNode; @@ -1524,6 +1525,26 @@ protected override unsafe void CreateHandle() } base.CreateHandle(); + +#pragma warning disable WFO5001 + if (Application.IsDarkModeEnabled) + { + // Get the TreeView's ToolTip handle: + HWND toolTipHandle = (HWND)PInvokeCore.SendMessage(HWND, PInvoke.TVM_GETTOOLTIPS, (WPARAM)0, (LPARAM)0); + PInvoke.SetWindowTheme(toolTipHandle, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + + // Round the corners of the ToolTip window. + if (OsVersion.IsWindows11_OrGreater()) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + toolTipHandle, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } + } +#pragma warning restore WFO5001 } /// @@ -1614,7 +1635,7 @@ internal void ForceScrollbarUpdate(bool delayed) /// /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. /// - internal override void SetToolTip(ToolTip toolTip) + internal override unsafe void SetToolTip(ToolTip toolTip) { if (toolTip is null || !ShowNodeToolTips) { From 7810b62a2ec359104c2b8a5627cdf87e1cb6b485 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Nov 2024 20:35:58 -0800 Subject: [PATCH 16/18] Darken the TabControl ToolTip in Dark mode --- .../Forms/Controls/TabControl/TabControl.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs index 618ce40bb14..06591c8179c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs @@ -7,6 +7,7 @@ using System.Drawing.Design; using System.Text; using System.Windows.Forms.Layout; +using Windows.Win32.Graphics.Dwm; using Windows.Win32.UI.Accessibility; namespace System.Windows.Forms; @@ -967,6 +968,26 @@ protected override unsafe void CreateHandle() } base.CreateHandle(); + +#pragma warning disable WFO5001 + if (Application.IsDarkModeEnabled) + { + // Get the TabControl's ToolTip handle: + HWND toolTipHandle = (HWND)PInvokeCore.SendMessage(HWND, PInvoke.TCM_GETTOOLTIPS, (WPARAM)0, (LPARAM)0); + PInvoke.SetWindowTheme(toolTipHandle, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + + // Round the corners of the ToolTip window. + if (OsVersion.IsWindows11_OrGreater()) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + toolTipHandle, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } + } +#pragma warning restore WFO5001 } private void DetachImageList(object? sender, EventArgs e) => ImageList = null; From 3bcc1d2923c1f2a461fe53a4928c3c603cae8ad2 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Nov 2024 21:48:04 -0800 Subject: [PATCH 17/18] Revert unnecessary change --- .../src/System/Windows/Forms/Controls/TreeView/TreeView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs index f26fdde2757..e177ff499d7 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs @@ -1635,7 +1635,7 @@ internal void ForceScrollbarUpdate(bool delayed) /// /// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed. /// - internal override unsafe void SetToolTip(ToolTip toolTip) + internal override void SetToolTip(ToolTip toolTip) { if (toolTip is null || !ShowNodeToolTips) { From 625896fa97f0368cccfed11e01a46f6298143794 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 12 Nov 2024 20:00:51 -0800 Subject: [PATCH 18/18] Move to OnHandleCreated --- .../Forms/Controls/TabControl/TabControl.cs | 40 +++++++++---------- .../Forms/Controls/TreeView/TreeView.cs | 40 +++++++++---------- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs index 06591c8179c..a6329bfe012 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs @@ -968,26 +968,6 @@ protected override unsafe void CreateHandle() } base.CreateHandle(); - -#pragma warning disable WFO5001 - if (Application.IsDarkModeEnabled) - { - // Get the TabControl's ToolTip handle: - HWND toolTipHandle = (HWND)PInvokeCore.SendMessage(HWND, PInvoke.TCM_GETTOOLTIPS, (WPARAM)0, (LPARAM)0); - PInvoke.SetWindowTheme(toolTipHandle, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); - - // Round the corners of the ToolTip window. - if (OsVersion.IsWindows11_OrGreater()) - { - DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; - PInvoke.DwmSetWindowAttribute( - toolTipHandle, - DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, - &roundSmall, - sizeof(DWM_WINDOW_CORNER_PREFERENCE)); - } - } -#pragma warning restore WFO5001 } private void DetachImageList(object? sender, EventArgs e) => ImageList = null; @@ -1242,7 +1222,7 @@ protected override void OnGotFocus(EventArgs e) /// We do some work here to configure the handle. /// Overriders should call base.OnHandleCreated() /// - protected override void OnHandleCreated(EventArgs e) + protected override unsafe void OnHandleCreated(EventArgs e) { if (!IsHandleCreated) { @@ -1280,6 +1260,24 @@ protected override void OnHandleCreated(EventArgs e) HWND.HWND_TOPMOST, 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE); + +#pragma warning disable WFO5001 + if (Application.IsDarkModeEnabled) + { + PInvoke.SetWindowTheme(tooltipHwnd, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + + // Round the corners of the ToolTip window. + if (OsVersion.IsWindows11_OrGreater()) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + tooltipHwnd, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } + } +#pragma warning restore WFO5001 } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs index e177ff499d7..709073ed50f 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs @@ -1525,26 +1525,6 @@ protected override unsafe void CreateHandle() } base.CreateHandle(); - -#pragma warning disable WFO5001 - if (Application.IsDarkModeEnabled) - { - // Get the TreeView's ToolTip handle: - HWND toolTipHandle = (HWND)PInvokeCore.SendMessage(HWND, PInvoke.TVM_GETTOOLTIPS, (WPARAM)0, (LPARAM)0); - PInvoke.SetWindowTheme(toolTipHandle, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); - - // Round the corners of the ToolTip window. - if (OsVersion.IsWindows11_OrGreater()) - { - DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; - PInvoke.DwmSetWindowAttribute( - toolTipHandle, - DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, - &roundSmall, - sizeof(DWM_WINDOW_CORNER_PREFERENCE)); - } - } -#pragma warning restore WFO5001 } /// @@ -1860,7 +1840,7 @@ protected override bool IsInputKey(Keys keyData) protected virtual void OnDrawNode(DrawTreeNodeEventArgs e) => _onDrawNode?.Invoke(this, e); - protected override void OnHandleCreated(EventArgs e) + protected override unsafe void OnHandleCreated(EventArgs e) { if (!IsHandleCreated) { @@ -1914,6 +1894,24 @@ protected override void OnHandleCreated(EventArgs e) { PInvokeCore.SendMessage(this, PInvoke.TVM_SETTEXTCOLOR, 0, c.ToWin32()); } + + if (Application.IsDarkModeEnabled) + { + // Get the TreeView's ToolTip handle: + HWND toolTipHandle = (HWND)PInvokeCore.SendMessage(HWND, PInvoke.TVM_GETTOOLTIPS, (WPARAM)0, (LPARAM)0); + PInvoke.SetWindowTheme(toolTipHandle, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + + // Round the corners of the ToolTip window. + if (OsVersion.IsWindows11_OrGreater()) + { + DWM_WINDOW_CORNER_PREFERENCE roundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL; + PInvoke.DwmSetWindowAttribute( + toolTipHandle, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &roundSmall, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } + } #pragma warning restore WFO5001 // Put the LineColor into the native control only if set.