Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix issue 11243 TreeNodeCollection.AddRange has been broken in .NET 8 and no longer results in TreeNodes being drawn at the correct location #11253

Merged
merged 10 commits into from
May 9, 2024

Conversation

Epica3055
Copy link
Member

@Epica3055 Epica3055 commented Apr 23, 2024

Fixes #11243

Screenshots

Before

image

After

image

Test methodology

  • manually

Test environment(s)

9.0.0-preview.4.24219.3

… and no longer results in TreeNodes being drawn at the correct location
@Epica3055 Epica3055 requested a review from a team as a code owner April 23, 2024 10:17
@Epica3055 Epica3055 marked this pull request as draft April 23, 2024 10:18
@dotnet-policy-service dotnet-policy-service bot added the draft draft PR label Apr 23, 2024
@@ -88,7 +88,8 @@ internal TreeNodeImageIndexer StateImageIndexer
}

internal int _index; // our index into our parents child array
internal List<TreeNode> _childNodes = [];
internal int _childCount;
internal TreeNode[] _children;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error CS8618: Non-nullable field '_children' must contain a non-null value when exiting constructor. Consider declaring the field as nullable

@Tanya-Solyanik
Copy link
Member

@Epica3055 , is this a straight revert of the #9078 PR? It would be good to understand what specific change introduced the regression. Once we know it, it will be easier to decide on the direction of the fix.

@@ -1250,20 +1250,20 @@ private void SortChildren(TreeView? parentTreeView)
continue;
}

if (sorter.Compare(_childNodes[j] /*previous*/, _childNodes[min] /*current*/) <= 0)
if (sorter.Compare(_children[j] /*previous*/, _children[min] /*current*/) <= 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (sorter.Compare(_children[j] /*previous*/, _children[min] /*current*/) <= 0)
if (sorter.Compare(previous: _children[j], current: _children[min]) <= 0)

(if these are indeed the parameter names)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually they are not.

@Epica3055 Epica3055 marked this pull request as ready for review April 28, 2024 08:05
Copy link

codecov bot commented Apr 29, 2024

Codecov Report

Attention: Patch coverage is 82.84024% with 29 lines in your changes are missing coverage. Please review.

Project coverage is 74.24623%. Comparing base (be78cbb) to head (f0ccdfa).
Report is 44 commits behind head on main.

Additional details and impacted files
@@                 Coverage Diff                 @@
##                main      #11253         +/-   ##
===================================================
- Coverage   74.32424%   74.24623%   -0.07802%     
===================================================
  Files           3011        3023         +12     
  Lines         625742      626382        +640     
  Branches       46547       46711        +164     
===================================================
- Hits          465078      465065         -13     
- Misses        157265      157917        +652     
- Partials        3399        3400          +1     
Flag Coverage Δ
Debug 74.24623% <82.84024%> (-0.07802%) ⬇️
integration 18.27645% <27.55906%> (-0.04969%) ⬇️
production 46.92204% <77.16535%> (-0.03560%) ⬇️
test 97.03764% <100.00000%> (-0.00375%) ⬇️
unit 43.87918% <77.16535%> (-0.02061%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

@Tanya-Solyanik
Copy link
Member

Please add a dedicated test for this scenario - call AddRange twice

@Tanya-Solyanik Tanya-Solyanik added the 📭 waiting-author-feedback The team requires more information from the author label Apr 29, 2024
@gpetrou
Copy link
Contributor

gpetrou commented May 1, 2024

Please check #11243 (comment). Also, even if there is still a problem, can't we still use a List instead of an array?

@Tanya-Solyanik
Copy link
Member

Please check #11243 (comment). Also, even if there is still a problem, can't we still use a List instead of an array?

Yes, this is a stable repro, when you call AddRange after the native tree view has been populated.

This code references a perf optimization for cases when we add large ranges to the treeview, and it seems that the workaround is to insert after the "fixed" node - the last node as previously populated treeview, as opposed to appending after the actual last node. To preserve this workaround as is, we would be inserting into the managed collection of child nodes at the "fixed index" as well, instead of appending, this seems complicated and an array is better suited for such use. To re-do this optimization, we need the specific scenario, Epica has experimented with large ranges and didn't see any difference with or without this perf optimization, I guess that scenario requires image lists, or other complications. But with no understanding of what this code is optimizing, it's risky to remove or modify it.

Copy link
Member

@Tanya-Solyanik Tanya-Solyanik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some minor comments, and please address the previous set of comments


form.Load += (s, e) =>
{
treeView.Nodes[0].Nodes.AddRange(treeNode1, treeNode2, treeNode3);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use rootNode local variable instead of the item accessor.

};
form.Show();

TreeNode childNode1 = treeView.Nodes[0].Nodes[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify that treeView.Nodes[0] is still the rootNode and then use rootNode local variable instead of the collection item accessor


form.Load += (s, e) =>
{
treeView.Nodes[0].Nodes.AddRange(treeNode1, treeNode2, treeNode3);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add 4 nodes to force collection resize, as far as I remember initial size is 4, and we are adding exactly 4 nodes conting child

@Tanya-Solyanik Tanya-Solyanik added the 📭 waiting-author-feedback The team requires more information from the author label May 7, 2024
@dotnet-policy-service dotnet-policy-service bot removed the 📭 waiting-author-feedback The team requires more information from the author label May 8, 2024
Copy link
Member

@Tanya-Solyanik Tanya-Solyanik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@Tanya-Solyanik Tanya-Solyanik added the ready-to-merge PRs that are ready to merge but worth notifying the internal team. label May 8, 2024
@Tanya-Solyanik Tanya-Solyanik merged commit b76827c into dotnet:main May 9, 2024
8 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the 9.0 Preview5 milestone May 9, 2024
@dotnet-policy-service dotnet-policy-service bot removed the ready-to-merge PRs that are ready to merge but worth notifying the internal team. label May 9, 2024
@Epica3055
Copy link
Member Author

/backport to release/8.0

Copy link
Contributor

Started backporting to release/8.0: https://github.com/dotnet/winforms/actions/runs/9040166498

Copy link
Contributor

@Epica3055 backporting to release/8.0 failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch

Applying: fix issue 11243 TreeNodeCollection.AddRange has been broken in .NET 8 and no longer results in TreeNodes being drawn at the correct location
Using index info to reconstruct a base tree...
A	src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeNode.cs
A	src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeNodeCollection.cs
Falling back to patching base and 3-way merge...
Auto-merging src/System.Windows.Forms/src/System/Windows/Forms/TreeNodeCollection.cs
CONFLICT (content): Merge conflict in src/System.Windows.Forms/src/System/Windows/Forms/TreeNodeCollection.cs
Auto-merging src/System.Windows.Forms/src/System/Windows/Forms/TreeNode.cs
CONFLICT (content): Merge conflict in src/System.Windows.Forms/src/System/Windows/Forms/TreeNode.cs
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 fix issue 11243 TreeNodeCollection.AddRange has been broken in .NET 8 and no longer results in TreeNodes being drawn at the correct location
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions github-actions bot locked and limited conversation to collaborators Jun 11, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
4 participants