diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/TreeNode.cs b/src/System.Windows.Forms/src/System/Windows/Forms/TreeNode.cs index ac104b072c7..15ed4dde3d8 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/TreeNode.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/TreeNode.cs @@ -93,7 +93,7 @@ internal TreeNodeImageIndexer StateImageIndexer } internal int index; // our index into our parents child array - internal readonly List childNodes = new(); + internal List childNodes = new(); internal TreeNode parent; internal TreeView treeView; private bool expandOnRealization; @@ -1249,23 +1249,83 @@ public static TreeNode FromHandle(TreeView tree, IntPtr handle) private void SortChildren(TreeView parentTreeView) { - var nodeCount = childNodes.Count; - if (nodeCount > 0) + if (childNodes.Count <= 0) { - List newOrder = new(nodeCount); - if (parentTreeView?.TreeViewNodeSorter is null) - { - childNodes.Sort((x, y) => Application.CurrentCulture.CompareInfo.Compare(x.Text, y.Text, CompareOptions.None)); - } - else + return; + } + + List newOrder = new(childNodes.Count); + if (parentTreeView is null || parentTreeView.TreeViewNodeSorter is null) + { + CompareInfo compare = Application.CurrentCulture.CompareInfo; + for (int i = 0; i < childNodes.Count; i++) { - childNodes.Sort(parentTreeView.TreeViewNodeSorter.Compare); + newOrder.Add(childNodes[i]); + + int min = -1; + for (int j = 0; j < childNodes.Count; j++) + { + if (childNodes[j] is null) + { + continue; + } + + if (min == -1) + { + min = j; + continue; + } + + if (compare.Compare(childNodes[j].Text, childNodes[min].Text) <= 0) + { + min = j; + } + } + + Debug.Assert(min != -1, "Bad sorting"); + newOrder[i] = childNodes[min]; + childNodes[min] = null!; + newOrder[i].index = i; + newOrder[i].SortChildren(parentTreeView); } + childNodes = newOrder; + } + else + { + IComparer sorter = parentTreeView.TreeViewNodeSorter; for (int i = 0; i < childNodes.Count; i++) { - childNodes[i].SortChildren(parentTreeView); + newOrder.Add(childNodes[i]); + + int min = -1; + for (int j = 0; j < childNodes.Count; j++) + { + if (childNodes[j] is null) + { + continue; + } + + if (min == -1) + { + min = j; + continue; + } + + if (sorter.Compare(childNodes[j] /*previous*/, childNodes[min] /*current*/) <= 0) + { + min = j; + } + } + + Debug.Assert(min != -1, "Bad sorting"); + newOrder[i] = childNodes[min]; + childNodes[min] = null!; + newOrder[i].index = i; + newOrder[i].SortChildren(parentTreeView); } + + childNodes = newOrder; } } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TreeViewTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TreeViewTests.cs index f55a72740f7..c63399439a4 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TreeViewTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/TreeViewTests.cs @@ -7114,6 +7114,52 @@ public void TreeView_TreeViewNodeSorter_ComparesTreeNodes() treeView.TreeViewNodeSorter = treeSorter; } + [WinFormsTheory] + [InlineData("A", "B")] + [InlineData("A", "A")] + [InlineData("B", "A")] + public void Clear_AfterSort_ShouldNotGetStuck(string firstNodeText, string secondNodeText) + { + using TreeView treeView = new(); + TreeNode parent = new("Parent"); + treeView.Nodes.Add(parent); + + TreeNode firstNode = new(firstNodeText); + parent.Nodes.Add(firstNode); + TreeNode secondNode = new(secondNodeText); + parent.Nodes.Add(secondNode); + + treeView.Sort(); + + Stopwatch timer = new(); + timer.Start(); + parent.Nodes.Clear(); + timer.Stop(); + Assert.True(timer.ElapsedMilliseconds <= 500); + } + + [WinFormsTheory] + [InlineData("A", "B")] + [InlineData("A", "A")] + [InlineData("B", "A")] + public void Remove_AfterSort_ShouldNotThrowException(string firstNodeText, string secondNodeText) + { + using TreeView treeView = new(); + TreeNode parent = new("Parent"); + treeView.Nodes.Add(parent); + + TreeNode firstNode = new(firstNodeText); + parent.Nodes.Add(firstNode); + TreeNode secondNode = new(secondNodeText); + parent.Nodes.Add(secondNode); + + treeView.Sort(); + + parent.Nodes.Remove(firstNode); + + parent.Nodes.Remove(secondNode); + } + private class SubTreeView : TreeView { public new bool CanEnableIme => base.CanEnableIme;