I have a UltraListView with many thousands of items in it. Its a very complicated list and I have it hooked into things like custom search and custom drag drop. So I rather not abandon UltraListView if I can avoid it. But I have hit a tough bottle neck with the UltraListView due to memory.
Each item in my list has a unique image that I generate from disk. In fact it turns out that for my application I need to generate eachi image from a file, one image per file. The files are in an internal format, not a standard image format. An additional constraint is that I want the image to be quite large. The user is often very interested in the details inside the image. So a really small image is not very useful. So each individual image is unique and large in memory.
So I populate the UltraListView with a pool of worker threads that scan the disk and generates all the unique image for each file found.
After about a minute, the list is loaded and then its fast. I can scroll and search and it works fine for my use case.
The down side is that preloading all images takes about 600mb of RAM. So I am getting out of memory exceptions. All those images eat all my spare memory and I dont have enough to run the rest of the app.
I need to stop preloading every single image in the list. I rather only have the images that are actually on screen loaded into RAM. At any given time there are only 50 or so images in view on the list. The rest of the images are all off screen but in memory. I think I need to implement some sort of "In View" cursor and stop preloading the images. I need some set of events that tell me at the UltraListViewItem level who is off screen and who is on screen.
I want an event handler that tells me that a particular UltraListViewItem goes on screen, then I can asychronously be fetched or generate the requisit image for that item. It would be acceptable for the the user to see a small delay as they 'pages in' on a scroll event.
I need another event that tells me a list item is off screen, so I can discard the non visible images and flush it from memory.
At most there only ever be N images loaded into RAM when N is like 3X the number items that can be visible.
So how can I do this? make a "In View" cursor with an UltraListView? Where can I get the on view events at the item level?
The content creation filter worked correctly but I found performance issues when I used it with my application. It turns out that IUIElementCreationFilter.AfterCreateChildElements() gets called many times. The filter in the sample iterates over every item in the list on every call and the loop checks each item to see if it needs to be either fetched or flushed. The iteration over the many thousands of items on every creation event was too expensive to have in a call back that getting called constantly What worked for me was a IUIElementDrawFilter.DrawElement(). This only gets for just each item on screen. So typically I only had to check a few dozen items to check if their respective image needed to be fetched from disk. Once the image was loaded, that item had an image would just do the default draw behavior on later calls to DrawElement. So that was faster and kept the UI responsive. But that still left me with the issue how to flush unused images. The whole point was to toss the offscreen images so the app would have enough RAM to run. So I made a homebrew garbage collector. I had a Timer that periodically fires a gcListImages() // called every 50 milliseconds in a timer int flushIndex; public void gcListImages() { int N = ultraListView1.Items.Count; if (N < 1) return; if (N <= flushIndex) flushIndex = 0; // filter has changed number of items
// examine a portion of the list release off screen images for(int i=0; i<250; ++i) { UltraListViewItem item = ultraListView1.Items[flushIndex]; // test if the image is off screen if (item.UIElement == null && item.Appearance.Image != null) { ModelData model = item.Tag as ModelData; // helper object for load and flush if (model != null) {
item.Appearance.Image = null; // release model.flushImage(); // that loads and dispose images } } flushIndex = (flushIndex + 1) % N; } }
Hello,
I am just checking about the progress of this issue. Let me know If you need my further assistance on this issue.
Thank you for using Infragistics Components.
Sorry this is the sample with the filter.
opps WS_UltraComboValidation.rar is not a filter sample. Perhapes you intended a different file?
So actually I got something half working. I implemented a
IUIElementDrawFilter
when DrawElement() gets call back by the control I can do the image fetch from disk on demand. So that gives me a very nice 'onScreen' event for each image. The down side of the draw filter approach for what I am doing is that there no clear 'off screen' event so its not obvious when to dispose the un used images with the draw filter. The basic idea is sound though. With the draw filter I only have the images I have actually viewed loaded. So that gets me most of the way there. Can the creation filter work with the draw filter and tell me when a particular images is 'done'? I will look at your sample when its uploaded.
What you could do in your case is to use Creation Filter. Every time the UltraListView redraws itself the two methods of the filter will be called so you can use it to update the images. If you want to know more about Creation Filter please follow this link:
http://help.infragistics.com/Help/Doc/WinForms/2012.2/CLR4.0/html/Win_Creation_Filter.html
In the AfterCreateChildElements method of the filter you can see which items have UIElement (so they are drawn on the screen) and have no image and add an image to them. Then you could see which items have no UIElement, but have an image and Dispose their image.
I have implemented a sample which demonstrates my suggestion.
Please let me know if you have any additional questions.