diff --git a/Winforms.sln b/Winforms.sln index c012dd45fe1..cfb4c21583a 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -202,6 +202,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Private.Windows.GdiPlus", "src\System.Private.Windows.GdiPlus\System.Private.Windows.GdiPlus.csproj", "{442C867C-51C0-8CE5-F067-DF065008E3DA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScratchProject2", "src\System.Windows.Forms\tests\IntegrationTests\ScratchProject2\ScratchProject2.csproj", "{9A195C79-D335-485B-B5AB-9A1EF1F9E58A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1102,6 +1104,22 @@ Global {442C867C-51C0-8CE5-F067-DF065008E3DA}.Release|x64.Build.0 = Release|Any CPU {442C867C-51C0-8CE5-F067-DF065008E3DA}.Release|x86.ActiveCfg = Release|Any CPU {442C867C-51C0-8CE5-F067-DF065008E3DA}.Release|x86.Build.0 = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|arm64.ActiveCfg = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|arm64.Build.0 = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|x64.ActiveCfg = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|x64.Build.0 = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|x86.ActiveCfg = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Debug|x86.Build.0 = Debug|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|Any CPU.Build.0 = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|arm64.ActiveCfg = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|arm64.Build.0 = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|x64.ActiveCfg = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|x64.Build.0 = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|x86.ActiveCfg = Release|Any CPU + {9A195C79-D335-485B-B5AB-9A1EF1F9E58A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE 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 5949a52df76..3a5c3124b7f 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 @@ -56,6 +56,8 @@ public partial class ListView : Control private View _viewStyle = View.LargeIcon; private string? _toolTipCaption = string.Empty; + private HBRUSH _hBrush; // To hold created dark mode brush for deletion + private const int LISTVIEWSTATE_ownerDraw = 0x00000001; private const int LISTVIEWSTATE_allowColumnReorder = 0x00000002; private const int LISTVIEWSTATE_autoArrange = 0x00000004; @@ -4331,6 +4333,12 @@ internal void ListViewItemToolTipChanged(ListViewItem item) protected virtual void OnAfterLabelEdit(LabelEditEventArgs e) { _onAfterLabelEdit?.Invoke(this, e); + + // Delete created _hBrush if it exists + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern void DeleteObject(HGDIOBJ ho); + DeleteObject(_hBrush); + } protected override void OnBackgroundImageChanged(EventArgs e) @@ -6908,6 +6916,32 @@ protected override void WndProc(ref Message m) { switch (m.MsgInternal) { + case PInvokeCore.WM_CTLCOLOREDIT: + // Default handling of edit label colors + m.ResultInternal = (LRESULT)0; +#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if (Application.IsDarkModeEnabled) + { + // Make background of dark mode edit labels the correct color + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern HBRUSH CreateSolidBrush(COLORREF color); + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern COLORREF SetBkColor(HDC hdc, COLORREF color); + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern COLORREF SetTextColor(HDC hdc, COLORREF color); + + Color tvColor = BackColor; + Color tvTextColor = ForeColor; + _hBrush = CreateSolidBrush(tvColor); + HDC editHDC = (HDC)m.WParamInternal; + SetBkColor(editHDC, tvColor); + SetTextColor(editHDC, tvTextColor); + LRESULT lrBrush = (LRESULT)(IntPtr)_hBrush; + m.ResultInternal = lrBrush; + } +#pragma warning restore WFO5001 + break; + case MessageId.WM_REFLECT_NOTIFY: WmReflectNotify(ref m); break; 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..bb524a1d3f5 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 @@ -12,6 +12,10 @@ using Windows.Win32.UI.Accessibility; using static System.Windows.Forms.TreeNode; + + + + namespace System.Windows.Forms; /// @@ -30,6 +34,9 @@ public partial class TreeView : Control private const string BackSlash = "\\"; private const int DefaultTreeViewIndent = 19; + // To hold created dark mode brush for deletion + private HBRUSH _hBrush; + private DrawTreeNodeEventHandler? _onDrawNode; private NodeLabelEditEventHandler? _onBeforeLabelEdit; private NodeLabelEditEventHandler? _onAfterLabelEdit; @@ -53,6 +60,8 @@ public partial class TreeView : Control private bool _hoveredAlready; private bool _rightToLeftLayout; + private HBRUSH _hBrush; // To hold created dark mode brush for deletion + private nint _mouseDownNode = 0; // ensures we fire nodeClick on the correct node private const int TREEVIEWSTATE_hideSelection = 0x00000001; @@ -2102,6 +2111,11 @@ protected virtual void OnAfterLabelEdit(NodeLabelEditEventArgs e) { e.Node.AccessibilityObject?.RaiseAutomationEvent(UIA_EVENT_ID.UIA_AutomationFocusChangedEventId); } + + // Delete created _hBrush if it exists + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern void DeleteObject(HGDIOBJ ho); + DeleteObject(_hBrush); } /// @@ -2551,6 +2565,38 @@ private unsafe void TvnSelected(NMTREEVIEWW* nmtv) } } + private enum DWMWINDOWATTRIBUTE + { + DWMWA_NCRENDERING_ENABLED = 1, + DWMWA_NCRENDERING_POLICY, + DWMWA_TRANSITIONS_FORCEDISABLED, + DWMWA_ALLOW_NCPAINT, + DWMWA_CAPTION_BUTTON_BOUNDS, + DWMWA_NONCLIENT_RTL_LAYOUT, + DWMWA_FORCE_ICONIC_REPRESENTATION, + DWMWA_FLIP3D_POLICY, + DWMWA_EXTENDED_FRAME_BOUNDS, + DWMWA_HAS_ICONIC_BITMAP, + DWMWA_DISALLOW_PEEK, + DWMWA_EXCLUDED_FROM_PEEK, + DWMWA_CLOAK, + DWMWA_CLOAKED, + DWMWA_FREEZE_REPRESENTATION, + DWMWA_PASSIVE_UPDATE_MODE, + DWMWA_USE_HOSTBACKDROPBRUSH, + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + DWMWA_WINDOW_CORNER_PREFERENCE = 33, + DWMWA_BORDER_COLOR, + DWMWA_CAPTION_COLOR, + DWMWA_TEXT_COLOR, + DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, + DWMWA_SYSTEMBACKDROP_TYPE, + DWMWA_LAST + } + + + private LRESULT TvnBeginLabelEdit(NMTVDISPINFOW nmtvdi) { // Check for invalid node handle @@ -2579,8 +2625,81 @@ private LRESULT TvnBeginLabelEdit(NMTVDISPINFOW nmtvdi) if (!e.CancelEdit) { _labelEdit = new TreeViewLabelEditNativeWindow(this); + _labelEdit.AssignHandle(PInvokeCore.SendMessage(this, PInvoke.TVM_GETEDITCONTROL)); } + + + + /* + // Force dark mode on editing label if necessary + [DllImport("dwmapi.dll", PreserveSig = false)] + static extern void DwmSetWindowAttribute(IntPtr hwnd, int attr, + ref int attrValue, int attrSize); + +#pragma warning disable WFO5001 + SystemColorMode thisColorMode; + if (Application.ColorMode == SystemColorMode.System) + { + thisColorMode = Application.SystemColorMode; + } + else + { + thisColorMode = Application.ColorMode; + } + + if (thisColorMode == SystemColorMode.Dark) + { + + + // Try different dwm attributes and values + // May be a problem with WIN 10 compatibility + + //int trueVal = 1; + + //var attr = (int)DWMWINDOWATTRIBUTE.DWMWA_CAPTION_COLOR; + //var attrVal = 0x00ff0000; + + IntPtr handle = _labelEdit.Handle; + Debug.WriteLine(handle.ToString()); + + for (int attrVal = 0; attrVal <= 10; attrVal++) + + { + Debug.WriteLine(""); + + for (int attr = 1; attr <= 50; attr++) + { + Debug.WriteLine(""); + try + { + + DwmSetWindowAttribute(handle, attr, ref attrVal, Marshal.SizeOf(attrVal)); + + + } + catch (Exception dwmEx) + { + string msg = dwmEx.Message; + if (dwmEx.InnerException is not null) + { + msg = msg + Environment.NewLine + dwmEx.InnerException.Message; + } + + Debug.WriteLine("Attribute:" + attr.ToString()); + Debug.WriteLine("Attr Valu:" + attrVal.ToString()); + Debug.WriteLine(msg); + } + } + } + } + + // do I need to invalidate to get an update? + + +#pragma warning restore WFO5001 + */ + return (LRESULT)(e.CancelEdit ? 1 : 0); } @@ -2782,6 +2901,7 @@ private unsafe void CustomDraw(ref Message m) // when one item is selected, click and hold on another). This needs to be fixed. Color riFore = renderinfo.ForeColor; Color riBack = renderinfo.BackColor; + if (renderinfo is not null && !riFore.IsEmpty) { nmtvcd->clrText = ColorTranslator.ToWin32(riFore); @@ -3160,6 +3280,33 @@ protected override unsafe void WndProc(ref Message m) { switch (m.MsgInternal) { + case PInvokeCore.WM_CTLCOLOREDIT: + // Default handling of edit label colors + m.ResultInternal = (LRESULT)0; + +#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if (Application.IsDarkModeEnabled) + { + // Make background of dark mode edit labels the correct color + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern HBRUSH CreateSolidBrush(COLORREF color); + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern COLORREF SetBkColor(HDC hdc, COLORREF color); + [DllImport("Gdi32.dll", PreserveSig = true)] + static extern COLORREF SetTextColor(HDC hdc, COLORREF color); + + Color tvColor = BackColor; + Color tvTextColor = ForeColor; + _hBrush = CreateSolidBrush(tvColor); + HDC editHDC = (HDC)m.WParamInternal; + SetBkColor(editHDC, tvColor); + SetTextColor(editHDC, tvTextColor); + LRESULT lrBrush = (LRESULT)(IntPtr)_hBrush; + m.ResultInternal = lrBrush; + } +#pragma warning restore WFO5001 + break; + case PInvokeCore.WM_WINDOWPOSCHANGING: case PInvokeCore.WM_NCCALCSIZE: case PInvokeCore.WM_WINDOWPOSCHANGED: diff --git a/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.Designer.cs b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.Designer.cs new file mode 100644 index 00000000000..20afa170cc2 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.Designer.cs @@ -0,0 +1,106 @@ +namespace ScratchProject2 +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + TreeNode treeNode1 = new TreeNode("Node1"); + TreeNode treeNode2 = new TreeNode("Node0", new TreeNode[] { treeNode1 }); + TreeNode treeNode3 = new TreeNode("Node3"); + TreeNode treeNode4 = new TreeNode("Node4"); + TreeNode treeNode5 = new TreeNode("Node5"); + TreeNode treeNode6 = new TreeNode("Node2", new TreeNode[] { treeNode3, treeNode4, treeNode5 }); + TreeNode treeNode7 = new TreeNode("Node6"); + ListViewItem listViewItem1 = new ListViewItem(new string[] { "1", "q", "w", "e" }, -1); + ListViewItem listViewItem2 = new ListViewItem(new string[] { "2", "a", "s", "d" }, -1); + ListViewItem listViewItem3 = new ListViewItem(new string[] { "3", "z", "x", "c" }, -1); + treeView1 = new TreeView(); + listView1 = new ListView(); + columnHeader1 = new ColumnHeader(); + columnHeader2 = new ColumnHeader(); + columnHeader3 = new ColumnHeader(); + SuspendLayout(); + // + // treeView1 + // + treeView1.LabelEdit = true; + treeView1.Location = new Point(24, 59); + treeView1.Name = "treeView1"; + treeNode1.Name = "Node1"; + treeNode1.Text = "Node1"; + treeNode2.Name = "Node0"; + treeNode2.Text = "Node0"; + treeNode3.Name = "Node3"; + treeNode3.Text = "Node3"; + treeNode4.Name = "Node4"; + treeNode4.Text = "Node4"; + treeNode5.Name = "Node5"; + treeNode5.Text = "Node5"; + treeNode6.Name = "Node2"; + treeNode6.Text = "Node2"; + treeNode7.Name = "Node6"; + treeNode7.Text = "Node6"; + treeView1.Nodes.AddRange(new TreeNode[] { treeNode2, treeNode6, treeNode7 }); + treeView1.Size = new Size(161, 199); + treeView1.TabIndex = 0; + + + // + // listView1 + // + listView1.Columns.AddRange(new ColumnHeader[] { columnHeader1, columnHeader2, columnHeader3 }); + listView1.Items.AddRange(new ListViewItem[] { listViewItem1, listViewItem2, listViewItem3 }); + listView1.LabelEdit = true; + listView1.Location = new Point(254, 61); + listView1.Name = "listView1"; + listView1.Size = new Size(159, 160); + listView1.TabIndex = 1; + listView1.UseCompatibleStateImageBehavior = false; + listView1.View = View.Details; + // + // Form1 + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(591, 334); + Controls.Add(listView1); + Controls.Add(treeView1); + Name = "Form1"; + Text = "Form1"; + ResumeLayout(false); + } + + #endregion + + private TreeView treeView1; + private ListView listView1; + + private ColumnHeader columnHeader1; + private ColumnHeader columnHeader2; + private ColumnHeader columnHeader3; + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.cs b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.cs new file mode 100644 index 00000000000..c8d36b187b4 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.cs @@ -0,0 +1,32 @@ + +using System.Diagnostics; + +namespace ScratchProject2 +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + Load += Form1_Load; + } + + private void Form1_Load(object? sender, EventArgs e) + { + treeView1.BeforeLabelEdit += treeView1_BeforeLabelEdit; + listView1.BeforeLabelEdit += listView1_BeforeLabelEdit; + } + + private void listView1_BeforeLabelEdit(object? sender, LabelEditEventArgs e) + { + Debug.WriteLine("ListView Here!"); + } + + + private void treeView1_BeforeLabelEdit(object? sender, NodeLabelEditEventArgs e) + { + Debug.WriteLine("TreeView Here!"); + } + + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.resx b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.resx new file mode 100644 index 00000000000..8b2ff64a113 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Program.cs b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Program.cs new file mode 100644 index 00000000000..21365c6d7c4 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/Program.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ScratchProject2; + +// This project is meant for temporary testing and experimenting and should be kept as simple as possible. + +internal static class Program +{ + [STAThread] + public static void Main() + { + Application.EnableVisualStyles(); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); +#pragma warning disable WFO5001 + Application.SetColorMode(SystemColorMode.Dark); +#pragma warning restore WFO5001 + Form1 form = new(); + Application.Run(form); + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/ScratchProject2.csproj b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/ScratchProject2.csproj new file mode 100644 index 00000000000..3d6e407ec73 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/ScratchProject2.csproj @@ -0,0 +1,35 @@ + + + + WinExe + enable + enable + true + true + + false + false + true + + + + + false + false + win-x64 + False + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/readme.txt b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/readme.txt new file mode 100644 index 00000000000..739914eee0e --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/ScratchProject2/readme.txt @@ -0,0 +1 @@ +This project is meant for doing temporary testing and should be left as simple and basic as possible. \ No newline at end of file