Hello,
I have a grid that is loading thumbnail images into cells in each row. When there are thousands of rows, performance is pretty slow. Is there a way to fill a cell or row only when it is displayed? I see there is a LoadOnDemand property for the Web grid, is there an equivalent for Win?
Can someone give any tips on how to implement something like this?
Thanks for the help!
Sean.
Hi Sean,
Where are the images coming from? Are they stored in the data source as actual images? Is the column's DataType Image? Or are the images coming from some sort of lookup and you using the Appearance on the cell to supply the image?
We are looking up the images on the file system and loading them in using the cell Appearance.
Okay, so I assume the row of data has some key field that you are using to determine the image to display. I also assume that you are loading the entire image into memory and then creating a thumbnail version for display in the grid.
There are a couple of approaches you can take to make this more efficient.
What I would do is - create some sort of caching mechanism. You could create a class that uses a Dictionary to store the key and the thumbnail. Then whenever you need a thumbnail, you pass the key to a method on this class. The class checks the dictionary to see if it already exists and if so, returns it. If not, the class goes out and gets the thumbnail and caches the key and the image in the dictionary so that the next time you ask for it, it's much faster.
That, of course, has nothing to do with the grid, but it's an optimization technique I have used in the past.
Regarding the grid itself, I recommend that you check out the WinGrid Performance Guide. In particular, the section on Memory Usage where it talks about re-using appearances.
If that's still not good enough, then another option would be to use a DrawFilter or CreationFilter. I'd probably go with the CreationFilter. You could replace the contents of the cell with a single ImageUIElement that fills the cell and apply your image to it based on the key value.
The advantage of this approach is that you only need to draw the images for cells that are actually displayed on-screen. The down side is that you will constantly be accessing the images every time the grid paints. So if you are going to take this approach, you pretty much have to use the caching mechanism I described above or else it will likely be even slower.
So in this approach, you do not use the Appearance on the cell at all, the DrawFilter handles the drawing of the cell based on the key value.
Those are great ideas, I will look into those. But my main hope was to not load all of the images initially. The problem we're having is that we're loading all of the images, which takes time, and the user may not even look at them because the UI has multiple tabs.
I do see there is an UltraGrid.DisplayLayout.LoadStyle property. I can't just set that to LoadOnDemand?
Great. Glad you got it all sorted out. :)
You're right, I did not need a custom ImageUIElement class. I had already created the "standard" implementation with adding and positioning the elements, so I stayed with that. It is working great. The image cache idea was another winner, working perfectly.
Mike, a big thanks to you for all your help! I was able to greatly improve the performance of our application, my boss is very pleased! Thanks!
Hi,
You do not need to create your own ImageUIElement class. What you should do is create a sort've "dummy" image that is the same size as your real thumbnails and then assign this image to the Appearance of the cell. The image can be completely blank (solid color) or even transparent. This will have the effect of creating an ImageUIElement in the cell already, which makes your CreationFilter a lot simpler. Now you don't have to worry about creating, adding, or positioning elements. All you have to do is examine the ImageUIElement which is already there and change it's Image property.
If you do that, then you will want to use AfterCreateChildElements. You can do this in one of two ways. You can either trap for the parent element which contains the ImageUIElement. In which case, you get the element and use GetDescendant to get the ImageUIElement from it. Or.. you can trap for when parent is an ImageUIElement and use GetContext to get the UltraGridCell from that. I'd probably go with the latter approach.
Once you have the ImageUIElement and the cell, it's easy. You examine the value of the cell and set the Image property on the ImageUIElement accordingly.
OK, found that I can access both the Row and Cell from the CellUIElement. Making good progress.
How do I know whether to set the image in BeforeCreateChildElements or AfterCreateChildElements?
Hi Mike,
I'm getting closer. The key for the image is the value of the cell that the image should be displayed in. How do I access that info in the CreationFilter class?
Thanks,