diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index e69de29bb2d..15deff718b6 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +override System.Windows.Forms.ButtonBase.OnSystemColorsChanged(System.EventArgs! e) -> void \ No newline at end of file diff --git a/src/System.Windows.Forms/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/System/Windows/Forms/Control.cs index 14efc5014ff..a03f36c1f58 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Control.cs @@ -12608,6 +12608,12 @@ protected virtual void WndProc(ref Message m) // Left here for debugging purposes. // string? text = m.LParamInternal == 0 ? null : new((char*)m.LParamInternal); + if (m.LParamInternal != 0 + && Application.ColorModeSet + && new string((char*)m.LParamInternal) is "ImmersiveColorSet") + { + OnSystemColorsChanged(EventArgs.Empty); + } if (action is SYSTEM_PARAMETERS_INFO_ACTION.SPI_SETNONCLIENTMETRICS && m.LParamInternal == 0) { diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs index 26c3292e175..a072c925d4d 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs @@ -1306,6 +1306,12 @@ private protected void UpdateOwnerDraw() } } + protected override void OnSystemColorsChanged(EventArgs e) + { + UpdateOwnerDraw(); + base.OnSystemColorsChanged(e); + } + /// /// Determines whether to use compatible text rendering engine (GDI+) or not (GDI). /// diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ButtonBaseTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ButtonBaseTests.cs index 51e37fbca43..acb051597c9 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ButtonBaseTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ButtonBaseTests.cs @@ -9246,6 +9246,51 @@ public void ButtonBase_WndProc_InvokeSetStateWithHandle_Success(FlatStyle flatSt Assert.Equal(0, createdCallCount); } + [WinFormsFact] + public static void ButtonBase_OnSystemColorsChanged_StandardFlatStyle_RestoresOwnerDraw() + { + using SubButtonBase control = new() + { + FlatStyle = FlatStyle.Standard + }; + + control.SetUserPaintAndUserMouseStyles(false); + + Assert.False(control.GetUserPaintStyle()); + Assert.False(control.GetUserMouseStyle()); + + control.OnSystemColorsChangedEntry(EventArgs.Empty); + + Assert.True(control.GetUserPaintStyle()); + Assert.True(control.GetUserMouseStyle()); + } + + [WinFormsFact] + public static void ButtonBase_OnSystemColorsChanged_SystemFlatStyle_ClassicMode_ClearsOwnerDraw() + { + RemoteExecutor.Invoke(static () => + { +#pragma warning disable WFO5001 + Application.SetColorMode(SystemColorMode.Classic); +#pragma warning restore WFO5001 + + using SubButtonBase control = new() + { + FlatStyle = FlatStyle.System + }; + + control.SetUserPaintAndUserMouseStyles(true); + + Assert.True(control.GetUserPaintStyle()); + Assert.True(control.GetUserMouseStyle()); + + control.OnSystemColorsChangedEntry(EventArgs.Empty); + + Assert.False(control.GetUserPaintStyle()); + Assert.False(control.GetUserMouseStyle()); + }).Dispose(); + } + private class SubButton : Button { public new bool GetStyle(ControlStyles flag) => base.GetStyle(flag); @@ -9377,6 +9422,17 @@ private class SubButtonBase : ButtonBase public new void SetStyle(ControlStyles flag, bool value) => base.SetStyle(flag, value); public new void WndProc(ref Message m) => base.WndProc(ref m); + + public void OnSystemColorsChangedEntry(EventArgs e) => OnSystemColorsChanged(e); + + public bool GetUserPaintStyle() => GetStyle(ControlStyles.UserPaint); + + public bool GetUserMouseStyle() => GetStyle(ControlStyles.UserMouse); + + public void SetUserPaintAndUserMouseStyles(bool value) + { + SetStyle(ControlStyles.UserPaint | ControlStyles.UserMouse, value); + } } protected override ButtonBase CreateButton() => new Button(); diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.cs index 2286bbff228..5dc4c1ac4b0 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.cs @@ -1201,6 +1201,54 @@ public void Control_Region_AssignNull_ShouldBeNull() control.Region.Should().BeNull(); } + [WinFormsFact] + public static void Control_WndProc_WM_SETTINGCHANGE_ImmersiveColorSet_RaisesSystemColorsChanged() + { + RemoteExecutor.Invoke(static () => + { +#pragma warning disable WFO5001 + Application.SetColorMode(SystemColorMode.System); +#pragma warning restore WFO5001 + + using SubSystemColorsChangedForm form = new(); + + int systemColorsChangedCallCount = 0; + form.SystemColorsChanged += (_, _) => systemColorsChangedCallCount++; + + Assert.NotEqual(IntPtr.Zero, form.Handle); + + form.SendWmSettingChange("ImmersiveColorSet"); + + Assert.Equal(1, systemColorsChangedCallCount); + Assert.Equal(1, form.OnSystemColorsChangedCallCount); + }).Dispose(); + } + + private class SubSystemColorsChangedForm : Form + { + public int OnSystemColorsChangedCallCount { get; private set; } + + public unsafe void SendWmSettingChange(string settingName) + { + fixed (char* settingNamePtr = settingName) + { + Message message = Message.Create( + Handle, + (int)PInvokeCore.WM_SETTINGCHANGE, + wparam: IntPtr.Zero, + lparam: (IntPtr)settingNamePtr); + + WndProc(ref message); + } + } + + protected override void OnSystemColorsChanged(EventArgs e) + { + OnSystemColorsChangedCallCount++; + base.OnSystemColorsChanged(e); + } + } + private class OnCreateControlCounter : Control { public int OnCreateControlCount { get; set; }