public ItemCacheManager(fetchDataCallbackHandler callback, int batchsize = 50, string debugName = "ItemCacheManager") { cacheBlocks = new List <CacheEntryBlock <T> >(); requests = 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 Windows.UI.Xaml.DispatcherTimer(); timer.Tick += (sender, args) => { fetchData(); }; timer.Interval = new TimeSpan(20 * 10000); #if DEBUG this.debugName = debugName; #endif #if TRACE_DATASOURCE Debug.WriteLine(debugName + "* Cache initialized/reset"); #endif }
/// <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); } #if TRACE_DATASOURCE string s = "┌ " + debugName + ".UpdateRanges: "; foreach (ItemIndexRange range in ranges) { s += range.FirstIndex + "->" + range.LastIndex + " "; } Debug.WriteLine(s); #endif //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 < this.cacheBlocks.Count && this.cacheBlocks[j].FirstIndex <= ranges[i].LastIndex) { ItemIndexRange overlap, oldEntryRange; ItemIndexRange[] added, removed; CacheEntryBlock <T> oldBlock = this.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); #if TRACE_DATASOURCE Debug.WriteLine("│ Transfering cache items " + overlap.FirstIndex + "->" + overlap.LastIndex); #endif } j++; if (ranges.Length > i + 1 && oldBlock.lastIndex < ranges[i + 1].FirstIndex) { lastTransferred = j; } } } //swap over to the new cache this.cacheBlocks = newCacheBlocks; //figure out what items need to be fetched because we don't have them in the cache this.requests = 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 this.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) { this.requests.Subtract(range); } this.cachedResults = newCachedResults; startFetchData(); #if TRACE_DATASOURCE s = "└ Pending requests: "; foreach (ItemIndexRange range in this.requests) { s += range.FirstIndex + "->" + range.LastIndex + " "; } Debug.WriteLine(s); #endif }