Skip to content
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

Style ToolTip to make it dark enough in Dark mode #12420

Open
wants to merge 18 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 @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1221,7 +1222,7 @@ protected override void OnGotFocus(EventArgs e)
/// We do some work here to configure the handle.
/// Overriders should call base.OnHandleCreated()
/// </summary>
protected override void OnHandleCreated(EventArgs e)
protected override unsafe void OnHandleCreated(EventArgs e)
{
if (!IsHandleCreated)
{
Expand Down Expand Up @@ -1259,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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1839,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)
{
Expand Down Expand Up @@ -1893,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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.ComponentModel;
using System.Drawing;
using Windows.Win32.Graphics.Dwm;

namespace System.Windows.Forms;

Expand Down Expand Up @@ -714,10 +715,30 @@ private unsafe void CreateHandle()

_window.CreateHandle(cp);

#pragma warning disable WFO5001
if (SystemInformation.HighContrast)
{
PInvoke.SetWindowTheme(HWND, string.Empty, string.Empty);
}
else if (Application.IsDarkModeEnabled)
{
if (!_isBalloon && 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,
$"{Control.DarkModeIdentifier}_{Control.ExplorerThemeIdentifier}");
}
#pragma warning restore WFO5001
}

// If in OwnerDraw mode, we don't want the default border.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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
/// <summary>
/// Scope for setting the default color mode (dark mode) for the application. Use in a <see langword="using"/> statement.
/// </summary>
public readonly ref struct ApplicationColorModeScope
{
private readonly SystemColorMode _originalColorMode;

public ApplicationColorModeScope(SystemColorMode colorMode)
{
_originalColorMode = Application.ColorMode;

if (_originalColorMode != colorMode)
{
Application.SetColorMode(colorMode);
}
}

public void Dispose()
{
if (Application.ColorMode != _originalColorMode)
{
Application.SetColorMode(_originalColorMode);
}
}
}
#pragma warning restore WFO5001
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Windows.Forms.Automation;
using Moq;
using Moq.Protected;
using Windows.Win32.Graphics.Dwm;

namespace System.Windows.Forms.Tests;

Expand Down Expand Up @@ -782,6 +783,44 @@ public void ToolTip_ToString_Invoke_ReturnsExpected()
Assert.Equal("System.Windows.Forms.ToolTip InitialDelay: 500, ShowAlways: False", toolTip.ToString());
}

#pragma warning disable WFO5001
[WinFormsTheory]
[BoolData]
public unsafe void ToolTip_DarkMode_GetCornerPreference_ReturnsExpected(bool value)
{
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()
{
IsBalloon = value,
};

toolTip.Handle.Should().NotBe(IntPtr.Zero); // A workaround to create the toolTip native window Handle

DWM_WINDOW_CORNER_PREFERENCE cornerPreference;
PInvoke.DwmGetWindowAttribute(
toolTip.HWND,
DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE,
&cornerPreference,
sizeof(DWM_WINDOW_CORNER_PREFERENCE));

cornerPreference.Should().Be(
toolTip.IsBalloon
? DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DEFAULT
: DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL);
}
#pragma warning restore WFO5001

[WinFormsFact]
public void ToolTip_SetToolTipToControl_Invokes_SetToolTip_OfControl()
{
Expand Down