public ItemCacheManager(FetchDataCallbackHandler <T> callback, int batchsize = 50) { _cacheBlocks = new List <CacheEntryBlock <T> >(); _request = new ItemIndexRangeList(); _cachedResults = new ItemIndexRangeList(); _fetchDataCallback = callback; _maxBatchFetchSize = batchsize; // set up a timer that is used to delay fetching data so that we can catch up if the list is scrolling fast _timer = new DispatcherTimer(); _timer.Tick += (sender, args) => { FetchData(); }; _timer.Interval = new TimeSpan(20 * 10000); }
/// <summary> /// Updates the desired item range of the cache, discarding items that are not needed, and figuring out which items /// need to be requested. It will then kick off a fetch if required. /// </summary> /// <param name="ranges">New set of ranges the cache should hold</param> public void UpdateRanges(ItemIndexRange[] ranges) { // Normalize ranges to get a unique set of discontinuous ranges ranges = NormalizeRanges(ranges); // Fail fast if the ranges haven't changed if (!HasRangesChanged(ranges)) { return; } // To make the cache update easier, we'll create a new set of CacheEntryBlocks List <CacheEntryBlock <T> > newCacheBlocks = new List <CacheEntryBlock <T> >(); foreach (ItemIndexRange range in ranges) { CacheEntryBlock <T> newBlock = new CacheEntryBlock <T> { FirstIndex = range.FirstIndex, Length = range.Length, Items = new T[range.Length] }; newCacheBlocks.Add(newBlock); } // Copy over data to the new cache blocks from the old ones where there is overlap int lastTransferred = 0; for (int i = 0; i < ranges.Length; i++) { CacheEntryBlock <T> newBlock = newCacheBlocks[i]; ItemIndexRange range = ranges[i]; int j = lastTransferred; while (j < _cacheBlocks.Count && _cacheBlocks[j].FirstIndex <= ranges[i].LastIndex) { ItemIndexRange overlap, oldEntryRange; ItemIndexRange[] added, removed; CacheEntryBlock <T> oldBlock = _cacheBlocks[j]; oldEntryRange = new ItemIndexRange(oldBlock.FirstIndex, oldBlock.Length); bool hasOverlap = oldEntryRange.DiffRanges(range, out overlap, out removed, out added); if (hasOverlap) { Array.Copy(oldBlock.Items, overlap.FirstIndex - oldBlock.FirstIndex, newBlock.Items, overlap.FirstIndex - range.FirstIndex, (int)overlap.Length); } j++; if (ranges.Length > i + 1 && oldBlock.LastIndex < ranges[i + 1].FirstIndex) { lastTransferred = j; } } } // swap over to the new cache _cacheBlocks = newCacheBlocks; // figure out what items need to be fetched because we don't have them in the cache _request = new ItemIndexRangeList(ranges); ItemIndexRangeList newCachedResults = new ItemIndexRangeList(); // Use the previous knowlege of what we have cached to form the new list foreach (ItemIndexRange range in ranges) { foreach (ItemIndexRange cached in _cachedResults) { ItemIndexRange overlap; ItemIndexRange[] added, removed; bool hasOverlap = cached.DiffRanges(range, out overlap, out removed, out added); if (hasOverlap) { newCachedResults.Add(overlap); } } } // remove the data we know we have cached from the results foreach (ItemIndexRange range in newCachedResults) { _request.Subtract(range); } _cachedResults = newCachedResults; StartFetchData(); }