[RESOLVED] FOR EACH ... bug/problem/quirk ....
Well i just found this one, (will be added to my article as soon as a decent patch/alternative can be found)...
look at the following code snip ...
Code:
For Each Childnode As TreeNode In TreeView1.Nodes
If Childnode.Nodes.Count = 0 Then
Childnode.Remove()
End If
Next
This looks just so simple ... If the node is empty delete it ...
NOPE ....
Simply put, it skips nodes... How And Why i do know ... but i need to find a way to do it... ( in a single pass)
A little background.
I have a treeview that has 9 base nodes (Groups), that get populated with specific data according to what token has been read..
Some tokens populate all 9 groups, some only 4, and others only 2. The groups are filled in a semi random way... all empty groups need to be removed from the tree, as they hold no useful info ..
HOW & WHY it skips nodes...
For those that are interested to know the how and why's ..
Think of the following 'FOR NEXT' Loop (On The Fly Code)
Code:
Total = Array.ItemCount
For X = 1 to Total
If Array(X) = 0 then
'Code to remove item from array
For Y = X to Total -1
Array(Y) = Array(Y+1)
Next Y
Total = Total -1
End If
Next X
Lets now validate this small loop ..
Array = {0, 0, 3, 4, 5, 6, 7, 8, 9, 0)
On the first pass we will get: -- X =1
Before: Array = {0, 0, 3, 4, 5, 6, 7, 8, 9, 0)
After :Array = {0, 3, 4, 5, 6, 7, 8, 9, 0 , 0)
But then on the second pass : -- X= 2
Before: Array = {0, 3, 4, 5, 6, 7, 8, 9, 0, 0)
After :Array = {0, 3, 4, 5, 6, 7, 8, 9, 0 , 0)
As you can see the second item is now acctualy the first one.. and with X = 2 we skip it ...
this is one reason why when deleting items from arrays you do it from the backwards.Last item first...
so the loop should be something along this line
Code:
Total = Array.ItemCount
For X = Total to 1 Step -1
If Array(X) = 0 then
'Code to remove item from array
For Y = X to Total -1
Array(Y) = Array(Y+1)
Next Y
Total = Total -1
End If
Next X
The problem here is that a 'FOR EACH' translates directly to a FOR Index = Min TO Max and suffers the delete skip problem ...
Any one have ideas appart from
Code:
For Index As Integer = TreeView1.Nodes.Count - 1 To 0 Step -1
Dim Childnode As TreeNode = TreeView1.Nodes(Index)
If Childnode.Nodes.Count = 0 Then
Childnode.Remove()
End If
Next
As this is what i've done already to solve the problem ..
However I'm looking for a NON INDEXED method .. IE.. For Each ...... Step -1..
Re: FOR EACH ... bug/problem/quirk ....
1) I thought that, in most cases, if you remove an item during a For Each, you got all sorts of strange results generally? I've had a number of programs crash when doing this kind of thing
2) A quick work around is to do this, but it doesn't really answer you question:
Code:
For Each Childnode As TreeNode In TreeView1.Nodes
Do While Childnode.Nodes.Count = 0
Childnode = Childnode.NextNode
Childnode.PrevNode.Remove()
Loop
Next
Re: FOR EACH ... bug/problem/quirk ....
Very nice try JavaJawa ... However... If the last item in the list is to be removed we land up with
Quote:
System.NullReferenceException was unhandled
Message="Object reference not set to an instance of an object."
and then the same will happen with the first item if we use
Code:
Childnode = Childnode.PrevNode
Childnode.NextNode.Remove()
The use of For Each is generally used for collections that are not indexed, or not sequentially indexed ...
something to note, in .NET now, just about any collection has a sequential index, that can be used via Collection.Item(Index), but often it is so much easier to use a For Each loop ....
Gremmy...
Re: FOR EACH ... bug/problem/quirk ....
Gremmy,
Modifying a collection invalidates the iterator. This is inherent in the design. The most common pattern is the gather/process pattern.
Code:
foreach (in real collection)
if (qualifier)
AttToRemovalList(...)
foreach (in removasllist)
RealCollection.Remove(...)
What prevents you from using this pattern????
The only alternative I can think of that is not "fragile" would be to implement a custom IEnumerable/IEnumerator for your collection....
Re: FOR EACH ... bug/problem/quirk ....
Quote:
Originally Posted by
TheCPUWizard
Gremmy,
Modifying a collection invalidates the iterator. This is inherent in the design. The most common pattern is the gather/process pattern.
Code:
foreach (in real collection)
if (qualifier)
AttToRemovalList(...)
foreach (in removasllist)
RealCollection.Remove(...)
What prevents you from using this pattern????
The only alternative I can think of that is not "fragile" would be to implement a custom IEnumerable/IEnumerator for your collection....
I hope you don't mind, but i'm going to use your phrase in my article, because it explains the conundrum very nicely... (Quoted of course)
Thanks..
Re: FOR EACH ... bug/problem/quirk ....
Quote:
Originally Posted by
GremlinSA
...
Code:
For Each Childnode As TreeNode In TreeView1.Nodes
If Childnode.Nodes.Count = 0 Then
Childnode.Remove()
End If
Next
...
Never used that control, so this may not work, but I would try:
Code:
For Each Childnode As TreeNode In TreeView1.Nodes.toarray 'or clone
If Childnode.Nodes.Count = 0 Then
TreeView1.nodes.item(Childnode.index).Remove()
End If
Next
Re: FOR EACH ... bug/problem/quirk ....
Quote:
Originally Posted by
Marraco
Never used that control, so this may not work, but I would try:
Code:
For Each Childnode As TreeNode In TreeView1.Nodes.toarray 'or clone
If Childnode.Nodes.Count = 0 Then
TreeView1.nodes.item(Childnode.index).Remove()
End If
Next
Not Childnode.index!. Maybe other identificator?
Re: FOR EACH ... bug/problem/quirk ....
Marroco...
No matter which way to try it, You still deleting/removing an item from the list, and this action is what is causing the problems..
Using the gather/ process method that CPU sugested, is the best method..
I've implemented it and it works 100s