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 #11266

Closed
wants to merge 2 commits into from

Conversation

Epica3055
Copy link
Member

@Epica3055 Epica3055 commented Apr 24, 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
int currentInd = _index;
int fixedInd = _parent.Nodes.FixedIndex;
Copy link
Member

Choose a reason for hiding this comment

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

What this code added in NET8?

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 no.

According to the comments I saw in the code, I think it was introduced to optimize AddRange performance.

But somehow after PR 9078, the logic about FixedIndex is not stable any more, which behaves like #11243.

So I tried to debug, and I found that the logic about FixedIndex didn't make scene to me about how to optimize performance and the logic is a little hard for me to understand. Then I tried to remove FixedIndex and I found that the issue is fixed.

I also made some tests like, using AddRange to add an array whose length is 1000. And I think it is not slow.

Copy link
Member

Choose a reason for hiding this comment

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

found that the logic about FixedIndex didn't make scene to me

Interesting, maybe the original logic was broken by the refactoring changes? Could you please review this logic in the NET Framework sources or on the referencesource website?

I'm concerned about removing performance optimization, some application might be relying on it.

I remember that PR that introduced the standard comparer had regressed something else in the TreeView, could you please review the file history and bugs related to the treeview? I wonder if it makes sence to revert the refactoring change?

Copy link
Member Author

@Epica3055 Epica3055 Apr 26, 2024

Choose a reason for hiding this comment

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

Yes, I checked NET Framework code. Before PR 9078 they are basically identical regarding AddRange logic and the code

The difference between before and after PR 9078 is here:

this is main branch

public virtual void AddRange(params TreeNode[] nodes)
{
ArgumentNullException.ThrowIfNull(nodes);
if (nodes.Length == 0)
{
return;
}
TreeView? tv = _owner.TreeView;
if (tv is not null && nodes.Length > TreeNode.MAX_TREENODES_OPS)
{
tv.BeginUpdate();
}
_owner.Nodes.FixedIndex = _owner._childNodes.Count;
_owner._childNodes.EnsureCapacity(nodes.Length);
for (int i = 0; i < nodes.Length; i++)
{
AddInternal(nodes[i], i);
}
_owner.Nodes.FixedIndex = -1;
if (tv is not null && nodes.Length > TreeNode.MAX_TREENODES_OPS)
{
tv.EndUpdate();
}
}

See line 224-227

public virtual void AddRange(TreeNode[] nodes)
{
ArgumentNullException.ThrowIfNull(nodes);
if (nodes.Length == 0)
{
return;
}
TreeView tv = owner.TreeView;
if (tv is not null && nodes.Length > TreeNode.MAX_TREENODES_OPS)
{
tv.BeginUpdate();
}
owner.Nodes.FixedIndex = owner.childCount;
owner.EnsureCapacity(nodes.Length);
for (int i = nodes.Length - 1; i >= 0; i--)
{
AddInternal(nodes[i], i);
}
owner.Nodes.FixedIndex = -1;
if (tv is not null && nodes.Length > TreeNode.MAX_TREENODES_OPS)
{
tv.EndUpdate();
}
}

this is 7.0 branch
See line 280-284


So now we use List<TreeNode> to store subTreeNodes instead of TreeNode[], so it's impossible to do reverse iteration.

But if we don't do reverse iteration the logic here will break down, which I think is the cause of this issue.

public TreeNode? PrevNode
{
get
{
if (_parent is null)
{
return null;
}
// fixedIndex is used for perf. optimization in case of adding big ranges of nodes
int currentInd = _index;
int fixedInd = _parent.Nodes.FixedIndex;
if (fixedInd > 0)
{
currentInd = fixedInd;
}
if (currentInd > 0 && currentInd <= _parent.Nodes.Count)
{
return _parent.Nodes[currentInd - 1];
}
else
{
return null;
}
}
}

I wonder if it makes sence to revert the refactoring change?

Yes, that's the solution that I came up with in the first place see PR 11253

Copy link
Member

@Tanya-Solyanik Tanya-Solyanik Apr 26, 2024

Choose a reason for hiding this comment

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

I see, we can't insert an item into a generic list at an index past the actual list size, even when within the capacity, like we were inserting into an empty portion of the array.
We can't revert the "FixedIndex" optimization because we don't know the specific scenario it solves. It might require a ListView with some specific properties and your test might not be sufficient.
I prefer reverting the change that replaced array of child nodes with the generic list. Also add an explanation comment as to why the List is not suitable in this case above the _children fiend declaration.

@Tanya-Solyanik Tanya-Solyanik added the 📭 waiting-author-feedback The team requires more information from the author label Apr 25, 2024
@dotnet-policy-service dotnet-policy-service bot removed the 📭 waiting-author-feedback The team requires more information from the author label Apr 26, 2024
@Tanya-Solyanik Tanya-Solyanik added the 📭 waiting-author-feedback The team requires more information from the author label Apr 26, 2024
@Epica3055 Epica3055 closed this Apr 28, 2024
@dotnet-policy-service dotnet-policy-service bot removed the 📭 waiting-author-feedback The team requires more information from the author label Apr 28, 2024
@github-actions github-actions bot locked and limited conversation to collaborators May 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
2 participants