I have a tree that I am generating that can possibly generate 1000's of nodes. Each node has an icon and text.
In situation where I have a couple of hundred, no problem. When I get into the 1000's, I get 'Out Of Memory' errors. If I take out the icons, no issue.
Not sure where to start with this, but I have an imagelist with 16x16 icons...png's. (Very small)
Here is an example of how I am generating certain levels of the tree:
UltraTreeNode worknode = null;
public void GenerateDash(UltraTree tv)
{
worknode = tv.GetNodeByKey(SQLReader.GetString(21) + SQLReader.GetInt32(24));if (worknode == null){if (sid) worknode = tv.Nodes[SQLReader.GetInt32(24) + "mc"].Nodes.Add(SQLReader.GetString(21) + SQLReader.GetInt32(24), SQLReader.GetString(21)); else worknode = tv.Nodes["me"].Nodes[SQLReader.GetInt32(24) + "emp"].Nodes[SQLReader.GetInt32(24) + "mc"].Nodes.Add(SQLReader.GetString(21) + SQLReader.GetInt32(24), SQLReader.GetString(21));worknode.LeftImages.Add(tv.ImageList.Images[12]); }if (tv.GetNodeByKey(SQLReader.GetString(21) + SQLReader.GetInt32(24) + SQLReader.GetString(22)) == null) { worknode = worknode.Nodes.Add(SQLReader.GetString(21) + SQLReader.GetInt32(24) + SQLReader.GetString(22), SQLReader.GetString(22)); worknode.LeftImages.Add(tv.ImageList.Images[35]);}else worknode = tv.GetNodeByKey(SQLReader.GetString(21) + SQLReader.GetInt32(24) + SQLReader.GetString(22));
}
Before this I had a standard .net tree which generated the same amount of nodes with the icon with no issue.
Thanks.
When you use the ImageList.ImageCollection indexer, it returns clones of the image at that location, essentially creating a new GDI resource each time you access the item at that position in the collection. Consider the following code:
ImageList im = new ImageList();im.Images.Add( new Bitmap(10, 10) );im.Images.Add( new Bitmap(10, 10) );im.Images.Add( new Bitmap(10, 10) );
Image img1 = im.Images[0];Image img2 = im.Images[0];bool same = Image.Equals(img1, img2);bool sameInstance = Image.ReferenceEquals(img1, img2);
If you run this code you will see that both 'same' and 'sameInstance' return false, because the ImageList creates new instance each time the return value from the indexer is assigned. This is why you get an OutOfMemoryException, and also why performance is affected, since the GDI resources are being strained, not to mention the heap fragmentation that comes with instantiating zillions of new objects.
The bottom line is: don't use the ImageList.ImageCollection indexer if you can help it, and use UltraTree's LeftImages/RightImages collection when possible, because we will access that same instance each time the image needs to be drawn, rather than going through the ImageList to get it.
The GetNodeByKey method is probably pretty expensive, too, by the way. This method has to loop through every node in the tree until it finds one with the correct key.
Of course, that won't cause an OutOfMemoryException, but it could affect performance if you are calling this method a lot.
Hello,
I have created a support case for you CAS-47241-GGDSXY, so you can send the sample in private and I will investigate it.
Thank you.
Sincerely,DimiDeveloper Support EngineerInfragistics, Inc.
Applying the changes you suggested has resolved the issue and things are MUCH faster now.
Though, I would like for you guys to take a look at an example method of how we are populating the tree but I would rather not post it here. Is there way I can email it? Basically, we are using a single instance of a node to do all of the building. We follow this same basic format for all of our tree construction.
worknode = tv.GetNodeByKey(xyz);
if(worknode != null)
worknode = worknode.add(some stuff);
)
else
worknode = worknode = tv.nodes.add(etc);
worknode.Tag = xyz;
worknode.LeftImages.Add(1);
worknode.DataKey = xyz;
worknode.Nodes.add(next database item)
melegant said:I have to tell you, that while I did have an image list associated to the tree, I changed worknode.LeftImages.Add(tv.ImageList.Images[12]) to your suggestion and it cut the time it takes the tree to generate in half.
That's interesting. I suspected that that might be the case, because when you specify an image, the tree cannot compare that image to any other image. So if you use tv.ImageList.Images[12] on two different nodes, the tree has no way of knowing that they are the same image. When you simply use the number 12 as an indexer into the ImageList, it knows the images are the same, so it can optimize things and re-use the same image.
melegant said: I have read your post regarding memory considerations for the ultragrid, is there one for the tree? We use a lot of the trees.
I don't believe there is any guide specific to the tree, but a lot of the same concepts from the WinGrid Performance Guide apply to the tree or other controls, too. The section on re-using Appearances, for example, applies to all of the controls. It does not apply to LeftImages and RightImages directly, since these are not Appearances, but re-using the images by indexing into the ImageList is essentially the same thing.
Is the current level of performance while using the indexes acceptable?
If not, then are you certain that the LeftImages is causing the major part of the performance issue? If you turn off the LeftImages entirely, is the performance acceptable at that point?
If that is the case, then there might be another potential solution using a DrawFilter. But 'm not sure if it will work. Try setting every node to the same image and see if that improves the performance. If it does, then what you could do is use the same image for every node and then use a DrawFilter to change the image. The advantage of this is that you only have to draw the images that are actually visible on the screen.
If assigning the same image to every node does not help, then you could still probably make this much faster by using a CreationFilter to add the ImageUIElement into the nodes yourself. Once again, this only has to operate on visible nodes, so it will be very fast and efficient. And I can help point you in the right direction in terms of coding the CreationFilter.
Still another option is that we could try to look into this and find out if the tree itself could be optimized to make this faster. In order to do that, we would need you to post a small sample project demonstrating the problem.