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 64203a7eb2b..d4490840e6f 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
@@ -2579,7 +2579,31 @@ private LRESULT TvnBeginLabelEdit(NMTVDISPINFOW nmtvdi)
if (!e.CancelEdit)
{
_labelEdit = new TreeViewLabelEditNativeWindow(this);
+ IntPtr editHandle = PInvokeCore.SendMessage(this, PInvoke.TVM_GETEDITCONTROL);
_labelEdit.AssignHandle(PInvokeCore.SendMessage(this, PInvoke.TVM_GETEDITCONTROL));
+
+ BeginInvoke((MethodInvoker)(() =>
+ {
+ // Use BeginInvoke to queue the operation for later execution,
+ // Ensures that the logic runs after the TreeView control's edit control is fully initialized.
+ // And this code adjusts the position and size of the edit control
+ // to accommodate the DPI scaling for high-resolution displays.
+ if (e.Node is not null)
+ {
+ float dpiScale = (float)DeviceDpi / ScaleHelper.OneHundredPercentLogicalDpi;
+
+ PInvoke.SetWindowPos(
+ (HWND)editHandle,
+ HWND.Null,
+ e.Node.Bounds.X,
+ e.Node.Bounds.Y,
+ e.Node.Bounds.Width + (int)(dpiScale * 10),
+ e.Node.Bounds.Height + 2,
+ SET_WINDOW_POS_FLAGS.SWP_NOZORDER
+ | SET_WINDOW_POS_FLAGS.SWP_FRAMECHANGED
+ | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);
+ }
+ }));
}
return (LRESULT)(e.CancelEdit ? 1 : 0);
@@ -2734,6 +2758,16 @@ private unsafe void CustomDraw(ref Message m)
// as background color.
if (_drawMode == TreeViewDrawMode.OwnerDrawText)
{
+ // When the node is being edited,
+ // set the text color and background color to the same color so that the text is hidden
+ // goto default to ensure the default drawing behavior is executed instead of a custom drawing.
+ if (node is { IsEditing: true })
+ {
+ nmtvcd->clrText = ColorTranslator.ToWin32(BackColor);
+ nmtvcd->clrTextBk = ColorTranslator.ToWin32(BackColor);
+ goto default;
+ }
+
nmtvcd->clrText = nmtvcd->clrTextBk;
m.ResultInternal = (LRESULT)(nint)(PInvoke.CDRF_NEWFONT | PInvoke.CDRF_NOTIFYPOSTPAINT);
return;
@@ -2792,6 +2826,11 @@ private unsafe void CustomDraw(ref Message m)
nmtvcd->clrTextBk = ColorTranslator.ToWin32(riBack);
}
+ if (node is { IsEditing: true })
+ {
+ nmtvcd->clrTextBk = ColorTranslator.ToWin32(BackColor);
+ }
+
if (renderinfo is not null && renderinfo.Font is not null)
{
// Mess with the DC directly...
@@ -2826,8 +2865,36 @@ private unsafe void CustomDraw(ref Message m)
{
Rectangle bounds = node.Bounds;
Size textSize = TextRenderer.MeasureText(node.Text, node.TreeView!.Font);
- Point textLoc = new(AppContextSwitches.MoveTreeViewTextLocationOnePixel ? bounds.X : bounds.X - 1, bounds.Y);
- bounds = new Rectangle(textLoc, new Size(textSize.Width, bounds.Height));
+ Point textLocation = new(AppContextSwitches.MoveTreeViewTextLocationOnePixel ? bounds.X : bounds.X - 1, bounds.Y);
+ bounds = new Rectangle(textLocation, new Size(textSize.Width, bounds.Height));
+
+ Rectangle fillRectangle = new Rectangle(textLocation, new(textSize.Width, bounds.Height));
+ Rectangle focusRectangle = new Rectangle(textLocation, new(textSize.Width, bounds.Height));
+
+ if (RightToLeft == RightToLeft.Yes && RightToLeftLayout)
+ {
+ int borderWidth = BorderStyle switch
+ {
+ BorderStyle.FixedSingle => 1,
+ BorderStyle.Fixed3D => 2,
+ _ => 0
+ };
+
+ // Reverse the X-axis drawing coordinates of the rectangle.
+ int invertedX = Width - bounds.X - textSize.Width - borderWidth * 2;
+
+ // Subtract the scroll bar width when the scroll bar appears.
+ if (Height - borderWidth * 2 < CalculatePreferredHeight())
+ {
+ float dpiScale = (float)DeviceDpi / (float)ScaleHelper.InitialSystemDpi;
+ invertedX -= (int)(SystemInformation.VerticalScrollBarWidth * Math.Round(dpiScale, 2));
+ }
+
+ // To ensure that the right side of the fillRectangle does not
+ // touch the left edge of the node prefix symbol, 1 pixel is subtracted here.
+ fillRectangle = new Rectangle(new Point(invertedX - 1, bounds.Y), new(textSize.Width, bounds.Height));
+ focusRectangle = new Rectangle(new Point(invertedX, bounds.Y), new(textSize.Width, bounds.Height));
+ }
DrawTreeNodeEventArgs e = new(g, node, bounds, (TreeNodeStates)(nmtvcd->nmcd.uItemState));
OnDrawNode(e);
@@ -2843,14 +2910,14 @@ private unsafe void CustomDraw(ref Message m)
// Draw the actual node.
if ((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected)
{
- g.FillRectangle(SystemBrushes.Highlight, bounds);
- ControlPaint.DrawFocusRectangle(g, bounds, color, SystemColors.Highlight);
+ g.FillRectangle(SystemBrushes.Highlight, fillRectangle);
+ ControlPaint.DrawFocusRectangle(g, focusRectangle, color, SystemColors.Highlight);
TextRenderer.DrawText(g, node.Text, font, bounds, color, TextFormatFlags.Default);
}
else
{
using var brush = BackColor.GetCachedSolidBrushScope();
- g.FillRectangle(brush, bounds);
+ g.FillRectangle(brush, fillRectangle);
TextRenderer.DrawText(g, node.Text, font, bounds, color, TextFormatFlags.Default);
}
@@ -2870,6 +2937,39 @@ private unsafe void CustomDraw(ref Message m)
}
}
+ ///
+ /// Calculate the preferred height of the entire tree.
+ ///
+ private int CalculatePreferredHeight()
+ {
+ // Nested method to recursively calculate the height of each node and its child nodes.
+ static int CountExpandedNodes(TreeNode node)
+ {
+ int count = 1; // Count the current node
+
+ // If the node is expanded, recursively calculate count its child nodes.
+ if (node.IsExpanded)
+ {
+ foreach (TreeNode childNode in node.Nodes)
+ {
+ count += CountExpandedNodes(childNode);
+ }
+ }
+
+ return count;
+ }
+
+ int expandedNodeCount = 0;
+ // Iterate through all top-level nodes to count the total number of expanded nodes.
+ foreach (TreeNode node in Nodes)
+ {
+ expandedNodeCount += CountExpandedNodes(node);
+ }
+
+ // Calculate the total height based on the number of expanded nodes.
+ return expandedNodeCount * ItemHeight;
+ }
+
///
/// Generates colors for each item. This can be overridden to provide colors on a per state/per node
/// basis, rather than using the ForeColor/BackColor/NodeFont properties on TreeNode.