public ItemCacheManager(fetchDataCallbackHandler callback, int batchsize = 1, 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)
        {
            // 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
        }