The key question is how to determine when images can be unloaded.
Let's take a naive example... If an image is visible, it'll be kept in memory, otherwise unload it.
(This won't be ideal as any time you scroll, images need to be re-fetched from disk but it demonstrates the approach)
Let's say we have a function that determines which images are in view and returns a list of paths [could equally be IDs]....
public List<string> GetVisibleImagePaths() {
//Do something here to return a dozen or so paths that are visible.
//Later refinement: Also include images a few either side of the current position for
//smoother scrolling
}
So....
When someone changes the scroll position of your control
protected override void OnScroll(ScrollEventArgs se) {
base.OnScroll(se);
EvictFromCache();
}
private Dictionary<string, Bitmap> imageCache;
private void EvictFromCache() {
var paths = GetVisibleImagePaths();
// Now loop through all the keys in the cache and evict those not specifically requested
// This is a naive approach to cache eviction. Perhaps you want to keep the last few dozen
// in case the user reverses direction and scrolls back or .... Lots of options, but for now
// we'll evict anything not in the list of paths we were given
foreach (var expired in imageCache.Keys.Where(x => !paths.Contains(x)).ToList()) {
//Dispose of the object, freeing up resources
imageCache[expired].Dispose();
//And now remove the reference to the disposed object
imageCache.Remove(expired);
}
}
You're going to have to do some tuning to work out the sweet spot for how much to keep in the cache/how far in advance to load images to give an optimal balance of performance/responsiveness on your target hardware.
Also, doing this on scroll isn't really necessary.... Just semi-regularly, but it was a convenient point to hook into for demo purposes. Depending on the strategy you pick, a timer or some other approach might be more appropriate.
One point on determining if something is disposed... Don't. As soon as you dispose of an object, release all references to it. If you need a new one, you'll have to create it fresh anyway, so keeping a reference to an unusable object is pointless.
Instead of checking if the object is disposed, check if you have an object referenced at all (is it null / does the key exist / is the list empty / etc)
This is handled above by disposing, then removing from the dictionary.