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..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 @@ -6973,6 +6973,18 @@ protected override void WndProc(ref Message m) WmReflectNotify(ref m); break; + case PInvokeCore.WM_CTLCOLOREDIT: + if (_labelEdit is not null && (HWND)m.LParamInternal == _labelEdit.HWND) + { + m.ResultInternal = (LRESULT)(nint)InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal); + } + else + { + base.WndProc(ref m); + } + + 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..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 @@ -3161,6 +3161,17 @@ protected override unsafe void WndProc(ref Message m) base.WndProc(ref m); } + break; + case PInvokeCore.WM_CTLCOLOREDIT: + if (_labelEdit is not null && (HWND)m.LParamInternal == _labelEdit.HWND) + { + m.ResultInternal = (LRESULT)(nint)InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal); + } + else + { + base.WndProc(ref m); + } + break; case PInvokeCore.WM_HSCROLL: base.WndProc(ref m); 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); } }