From 5d6bf1a8b1d6e1c4ef1d82d1030f6a9c7647e7d7 Mon Sep 17 00:00:00 2001 From: Sathish-087 Date: Mon, 8 Jun 2026 10:53:33 +0530 Subject: [PATCH 1/3] Added fix for issue 12042 --- .../Windows/Forms/Controls/ListView/ListView.cs | 12 ++++++++++++ .../Windows/Forms/Controls/TreeView/TreeView.cs | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs index f290e8ce8eb..399838de3c0 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs @@ -6973,6 +6973,18 @@ protected override void WndProc(ref Message m) WmReflectNotify(ref m); break; + case PInvokeCore.WM_CTLCOLOREDIT: + if (Application.IsDarkModeEnabled && DarkModeRequestState is true) + { + IntPtr hdc = (nint)m.WParamInternal; + int backColor = ColorTranslator.ToWin32(SystemColors.Window); + int textColor = ColorTranslator.ToWin32(SystemColors.WindowText); + PInvokeCore.SetTextColor((HDC)hdc, textColor); + PInvokeCore.SetBkColor((HDC)hdc, backColor); + } + + break; + case PInvokeCore.WM_KEYUP: var key = (VIRTUAL_KEY)(uint)m.WParamInternal; 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 4d6a3c53602..8756cc73d6a 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 @@ -3161,6 +3161,17 @@ protected override unsafe void WndProc(ref Message m) base.WndProc(ref m); } + break; + case PInvokeCore.WM_CTLCOLOREDIT: + if (Application.IsDarkModeEnabled && DarkModeRequestState is true) + { + IntPtr hdc = (nint)m.WParamInternal; + int backColor = ColorTranslator.ToWin32(SystemColors.Window); + int textColor = ColorTranslator.ToWin32(SystemColors.WindowText); + PInvokeCore.SetTextColor((HDC)hdc, textColor); + PInvokeCore.SetBkColor((HDC)hdc, backColor); + } + break; case PInvokeCore.WM_HSCROLL: base.WndProc(ref m); From f27c999cede0c11ab644dd0a2fab0a5959f22fe6 Mon Sep 17 00:00:00 2001 From: Sathish-087 Date: Tue, 14 Jul 2026 17:01:17 +0530 Subject: [PATCH 2/3] Address Review Comments and Added Unit TestCases --- .../Forms/Controls/ListView/ListView.cs | 16 +++-- .../Forms/Controls/TreeView/TreeView.cs | 16 +++-- .../System/Windows/Forms/ListViewTests.cs | 70 +++++++++++++++++++ .../System/Windows/Forms/TreeViewTests.cs | 58 +++++++++++++++ 4 files changed, 148 insertions(+), 12 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs index 399838de3c0..6bccb0eb959 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs @@ -6974,13 +6974,17 @@ protected override void WndProc(ref Message m) break; case PInvokeCore.WM_CTLCOLOREDIT: - if (Application.IsDarkModeEnabled && DarkModeRequestState is true) + // The native label-edit control created for in-place item editing is a plain Win32 Edit + // control, not a WinForms Control, so the base WmCtlColorControl handling (which looks it up + // via FromHandle) can't find it and falls back to default (light-themed) system rendering. + // Explicitly color it to match this ListView's own (already dark-mode-aware) colors. + if (_labelEdit is not null && (HWND)m.LParamInternal == _labelEdit.HWND) { - IntPtr hdc = (nint)m.WParamInternal; - int backColor = ColorTranslator.ToWin32(SystemColors.Window); - int textColor = ColorTranslator.ToWin32(SystemColors.WindowText); - PInvokeCore.SetTextColor((HDC)hdc, textColor); - PInvokeCore.SetBkColor((HDC)hdc, backColor); + m.ResultInternal = (LRESULT)(nint)InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal); + } + else + { + base.WndProc(ref m); } break; 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 8756cc73d6a..d68da5bca26 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 @@ -3163,13 +3163,17 @@ protected override unsafe void WndProc(ref Message m) break; case PInvokeCore.WM_CTLCOLOREDIT: - if (Application.IsDarkModeEnabled && DarkModeRequestState is true) + // The native label-edit control created for in-place item editing is a plain Win32 Edit + // control, not a WinForms Control, so the base WmCtlColorControl handling (which looks it up + // via FromHandle) can't find it and falls back to default (light-themed) system rendering. + // Explicitly color it to match this ListView's own (already dark-mode-aware) colors. + if (_labelEdit is not null && (HWND)m.LParamInternal == _labelEdit.HWND) { - IntPtr hdc = (nint)m.WParamInternal; - int backColor = ColorTranslator.ToWin32(SystemColors.Window); - int textColor = ColorTranslator.ToWin32(SystemColors.WindowText); - PInvokeCore.SetTextColor((HDC)hdc, textColor); - PInvokeCore.SetBkColor((HDC)hdc, backColor); + m.ResultInternal = (LRESULT)(nint)InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal); + } + else + { + base.WndProc(ref m); } break; diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListViewTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListViewTests.cs index adad3bdfe51..33b801723b3 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListViewTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListViewTests.cs @@ -6066,6 +6066,74 @@ public void ListView_GroupTaskLinkClick_EventHandling_ShouldBehaveAsExpected() callCount.Should().Be(4); } + [WinFormsTheory] + [InlineData(0x1E, 0x1E, 0x1E, 0xFF, 0xFF, 0xFF)] // Dark mode: dark bg, white text + [InlineData(0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00)] // Light mode: white bg, black text + public void ListView_WmCtlColorEdit_WithMatchingLabelEditHwnd_SetsDCColorsAndReturnsNonZeroBrush(int backR, int backG, int backB, int foreR, int foreG, int foreB) + { + // Regression test for issue #12042 + Color expectedBackColor = Color.FromArgb(backR, backG, backB); + Color expectedForeColor = Color.FromArgb(foreR, foreG, foreB); + + using SubListView listView = new() + { + LabelEdit = true, + View = View.Details, + Size = new Size(300, 200), + BackColor = expectedBackColor, + ForeColor = expectedForeColor + }; + + listView.Columns.Add(new ColumnHeader { Text = "Column 1", Width = 200 }); + listView.Items.Add(new ListViewItem("Item 1")); + listView.CreateControl(); + + // Select the item then trigger label editing via the timer (same pattern used in + // ListView_OnMouseClick_EditLabel_AsExpected). + PInvoke.SetFocus(listView); + listView.Items[0].Selected = true; + + Point itemLocation = listView.Items[0].Bounds.Location + new Size(1, 1); + PInvokeCore.PostMessage(listView, PInvokeCore.WM_LBUTTONUP, 0, PARAM.FromPoint(itemLocation)); + PInvokeCore.SendMessage(listView, PInvokeCore.WM_LBUTTONDOWN, 1, PARAM.FromPoint(itemLocation)); + PInvokeCore.SendMessage(listView, PInvokeCore.WM_TIMER, + (WPARAM)(nint)listView.TestAccessor.Dynamic.LVLABELEDITTIMER); + + HWND editHwnd = (HWND)(nint)PInvokeCore.SendMessage(listView, PInvoke.LVM_GETEDITCONTROL); + + if (editHwnd == HWND.Null) + { + PInvokeCore.SendMessage(listView, PInvoke.LVM_CANCELEDITLABEL); + return; + } + + using GetDcScope hdc = new(editHwnd); + Message msg = Message.Create( + listView.Handle, + (int)PInvokeCore.WM_CTLCOLOREDIT, + (nint)(HDC)hdc, + (nint)editHwnd); + + listView.WndProc(ref msg); + + // 1. A valid (non-null) brush handle must be returned. + Assert.NotEqual(IntPtr.Zero, (nint)msg.ResultInternal); + + // 2. DC text color must match the ForeColor we set on the control. + Color actualTextColor = PInvoke.GetTextColor(hdc); + Assert.Equal( + ColorTranslator.ToWin32(expectedForeColor), + ColorTranslator.ToWin32(actualTextColor)); + + // 3. DC background color must match the BackColor we set on the control. + Color actualBkColor = PInvoke.GetBkColor(hdc); + Assert.Equal( + ColorTranslator.ToWin32(expectedBackColor), + ColorTranslator.ToWin32(actualBkColor)); + + PInvokeCore.SendMessage(listView, PInvoke.LVM_CANCELEDITLABEL); + } + private class SubListViewItem : ListViewItem { public AccessibleObject CustomAccessibleObject { get; set; } @@ -6171,6 +6239,8 @@ private class SubListView : ListView public new void OnCacheVirtualItems(CacheVirtualItemsEventArgs e) => base.OnCacheVirtualItems(e); public new void OnItemChecked(ItemCheckedEventArgs e) => base.OnItemChecked(e); + + public new void WndProc(ref Message m) => base.WndProc(ref m); } private SubListView GetSubListViewWithData(View view, bool virtualMode, bool showGroups, bool withinGroup, bool createControl) diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/TreeViewTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/TreeViewTests.cs index 230d7b81490..6d0e56073ee 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/TreeViewTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/TreeViewTests.cs @@ -7569,6 +7569,62 @@ public void TreeView_VisibleCount_MultipleNodes_ReturnsExpected(int nodeCount) treeView.VisibleCount.Should().Be(5); } + [WinFormsTheory] + [InlineData(0x1E, 0x1E, 0x1E, 0xFF, 0xFF, 0xFF)] // Dark mode: dark bg, white text + [InlineData(0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00)] // Light mode: white bg, black text + public void TreeView_WmCtlColorEdit_WithMatchingLabelEditHwnd_SetsDCColorsAndReturnsNonZeroBrush(int backR, int backG, int backB, int foreR, int foreG, int foreB) + { + // Regression test for issue #12042 + Color expectedBackColor = Color.FromArgb(backR, backG, backB); + Color expectedForeColor = Color.FromArgb(foreR, foreG, foreB); + + using SubTreeView treeView = new() + { + LabelEdit = true, + Size = new Size(200, 200), + BackColor = expectedBackColor, + ForeColor = expectedForeColor + }; + + treeView.Nodes.Add("Node0"); + treeView.CreateControl(); + + treeView.Nodes[0].BeginEdit(); + HWND editHwnd = (HWND)(nint)PInvokeCore.SendMessage(treeView, PInvoke.TVM_GETEDITCONTROL); + + if (editHwnd == HWND.Null) + { + treeView.Nodes[0].EndEdit(cancel: true); + return; + } + + using GetDcScope hdc = new(editHwnd); + Message msg = Message.Create( + treeView.Handle, + (int)PInvokeCore.WM_CTLCOLOREDIT, + (nint)(HDC)hdc, + (nint)editHwnd); + + treeView.WndProc(ref msg); + + // 1. A valid (non-null) brush handle must be returned. + Assert.NotEqual(IntPtr.Zero, (nint)msg.ResultInternal); + + // 2. DC text color must match the ForeColor we set on the control. + Color actualTextColor = PInvoke.GetTextColor(hdc); + Assert.Equal( + ColorTranslator.ToWin32(expectedForeColor), + ColorTranslator.ToWin32(actualTextColor)); + + // 3. DC background color must match the BackColor we set on the control. + Color actualBkColor = PInvoke.GetBkColor(hdc); + Assert.Equal( + ColorTranslator.ToWin32(expectedBackColor), + ColorTranslator.ToWin32(actualBkColor)); + + treeView.Nodes[0].EndEdit(cancel: true); + } + private class SubTreeView : TreeView { public new bool CanEnableIme => base.CanEnableIme; @@ -7674,5 +7730,7 @@ private class SubTreeView : TreeView public new void OnNodeMouseHover(TreeNodeMouseHoverEventArgs e) => base.OnNodeMouseHover(e); public new void OnRightToLeftLayoutChanged(EventArgs e) => base.OnRightToLeftLayoutChanged(e); + + public new void WndProc(ref Message m) => base.WndProc(ref m); } } From b017f03f6bb1962bca1d9c2c57a473f0c37057a2 Mon Sep 17 00:00:00 2001 From: Sathish-087 Date: Tue, 14 Jul 2026 17:08:46 +0530 Subject: [PATCH 3/3] Remove comments --- .../System/Windows/Forms/Controls/ListView/ListView.cs | 4 ---- .../System/Windows/Forms/Controls/TreeView/TreeView.cs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs index 6bccb0eb959..e21b746f6f4 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs @@ -6974,10 +6974,6 @@ protected override void WndProc(ref Message m) break; case PInvokeCore.WM_CTLCOLOREDIT: - // The native label-edit control created for in-place item editing is a plain Win32 Edit - // control, not a WinForms Control, so the base WmCtlColorControl handling (which looks it up - // via FromHandle) can't find it and falls back to default (light-themed) system rendering. - // Explicitly color it to match this ListView's own (already dark-mode-aware) colors. if (_labelEdit is not null && (HWND)m.LParamInternal == _labelEdit.HWND) { m.ResultInternal = (LRESULT)(nint)InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal); 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 d68da5bca26..58308b6c78b 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 @@ -3163,10 +3163,6 @@ protected override unsafe void WndProc(ref Message m) break; case PInvokeCore.WM_CTLCOLOREDIT: - // The native label-edit control created for in-place item editing is a plain Win32 Edit - // control, not a WinForms Control, so the base WmCtlColorControl handling (which looks it up - // via FromHandle) can't find it and falls back to default (light-themed) system rendering. - // Explicitly color it to match this ListView's own (already dark-mode-aware) colors. if (_labelEdit is not null && (HWND)m.LParamInternal == _labelEdit.HWND) { m.ResultInternal = (LRESULT)(nint)InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal);