// Called after a reset to figure out the new indexes for the selected items private async Task RemapSelection() { ItemIndexRangeList oldSelection = selection; ItemCacheManager <string> oldSelectionCache = selectionCache; ItemIndexRangeList newSelection = new ItemIndexRangeList(); ItemCacheManager <string> newSelectionCache = new ItemCacheManager <string>(fetchSelectionDataCallback, 50, "newSelectionCache"); foreach (ItemIndexRange r in oldSelection) { IReadOnlyList <StorageFile> results = null; int lastResultOffset = 0, lastResultsItemIndex = 0; for (int i = 0; i < r.Length; i++) { int origIndex = r.FirstIndex + i; string fileid = oldSelectionCache[origIndex]; bool matched = false; // Optimization to be able to work in batches. Once we get a batch of files from the filesystem we use that to hunt // for matches rather than having to go ask for the index of each file if (results != null) { for (int j = lastResultOffset + 1; j < results.Count; j++) { if (results[j].FolderRelativeId == fileid) { lastResultOffset = j; int itemIndex = lastResultsItemIndex + j; newSelection.Add((uint)itemIndex, 1); newSelectionCache[itemIndex] = oldSelectionCache[origIndex]; matched = true; break; } } } if (!matched) { // Get a starting point for the index of the file lastResultsItemIndex = (int)(await _queryResult.FindStartIndexAsync(fileid.Substring(fileid.LastIndexOf('\\') + 1))); lastResultOffset = 0; // Get the files at that point and see if the keys actually match results = await _queryResult.GetFilesAsync((uint)lastResultsItemIndex, 50); if (results[lastResultOffset].FolderRelativeId == fileid) { newSelection.Add((uint)lastResultsItemIndex, 1); newSelectionCache[lastResultsItemIndex] = oldSelectionCache[origIndex]; } else { // We can't find the item, so its no longer part of the selection } } } } // Update the fields for the new selection selection = newSelection; selectionCache = newSelectionCache; }
/// <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 }
// Called when an item (or items) are selected public void SelectRange(ItemIndexRange range) { selection.Add(range); selectionCache.UpdateRanges(selection.ToArray()); }