public static bool DiffRanges(this ItemIndexRange RangeA, ItemIndexRange RangeB, out ItemIndexRange InBothAandB, out ItemIndexRange[] OnlyInRangeA, out ItemIndexRange[] OnlyInRangeB) { List<ItemIndexRange> exA = new List<ItemIndexRange>(); List<ItemIndexRange> exB = new List<ItemIndexRange>(); int i, j; i = Math.Max(RangeA.FirstIndex, RangeB.FirstIndex); j = Math.Min(RangeA.LastIndex, RangeB.LastIndex); if (i <= j) { // Ranges intersect InBothAandB = new ItemIndexRange(i, (uint)(1 + j - i)); if (RangeA.FirstIndex < i) exA.Add(new ItemIndexRange(RangeA.FirstIndex, (uint)(i - RangeA.FirstIndex))); if (RangeA.LastIndex > j) exA.Add(new ItemIndexRange(j + 1, (uint)(RangeA.LastIndex - j))); if (RangeB.FirstIndex < i) exB.Add(new ItemIndexRange(RangeB.FirstIndex, (uint)(i - RangeB.FirstIndex))); if (RangeB.LastIndex > j) exB.Add(new ItemIndexRange(j + 1, (uint)(RangeB.LastIndex - j))); OnlyInRangeA = exA.ToArray(); OnlyInRangeB = exB.ToArray(); return true; } else { InBothAandB = default(ItemIndexRange); OnlyInRangeA = new ItemIndexRange[] { RangeA }; OnlyInRangeB = new ItemIndexRange[] { RangeB }; return false; } }
public static ItemIndexRange Combine(this ItemIndexRange itemIndexRange, ItemIndexRange range) { int start = Math.Min(itemIndexRange.FirstIndex, range.FirstIndex); int end = Math.Max(itemIndexRange.LastIndex, range.LastIndex); return new ItemIndexRange(start, 1 + (uint)Math.Abs(end - start)); }
public static ItemIndexRange Overlap(this ItemIndexRange RangeA, ItemIndexRange RangeB) { int i, j; i = Math.Max(RangeA.FirstIndex, RangeB.FirstIndex); j = Math.Min(RangeA.LastIndex, RangeB.LastIndex); if (i <= j) { // Ranges intersect return new ItemIndexRange(i, (uint)(1 + j - i)); } else { return null; } }
static public bool Intersects(this ItemIndexRange me, ItemIndexRange range) { return((range.FirstIndex >= me.FirstIndex && range.FirstIndex <= me.LastIndex) || (range.LastIndex >= me.FirstIndex && range.LastIndex <= me.LastIndex)); }
static public bool GreaterThan(this ItemIndexRange me, ItemIndexRange range) { return(me.FirstIndex > range.FirstIndex || (me.FirstIndex == range.FirstIndex && me.LastIndex > range.LastIndex)); }
static public IList <ItemIndexRange> Subtract(this IList <ItemIndexRange> ranges, ItemIndexRange range) { return(Subtract((IEnumerable <ItemIndexRange>)ranges, range).ToList()); }
public void DeselectRange(ItemIndexRange itemIndexRange) { _rangeSelection = _rangeSelection.Subtract(itemIndexRange); }
public static bool DiffRanges(this ItemIndexRange RangeA, ItemIndexRange RangeB, out ItemIndexRange InBothAandB, out ItemIndexRange[] OnlyInRangeA, out ItemIndexRange[] OnlyInRangeB) { List <ItemIndexRange> exA = new List <ItemIndexRange>(); List <ItemIndexRange> exB = new List <ItemIndexRange>(); int i, j; i = Math.Max(RangeA.FirstIndex, RangeB.FirstIndex); j = Math.Min(RangeA.LastIndex, RangeB.LastIndex); if (i <= j) { // Ranges intersect InBothAandB = new ItemIndexRange(i, (uint)(1 + j - i)); if (RangeA.FirstIndex < i) { exA.Add(new ItemIndexRange(RangeA.FirstIndex, (uint)(i - RangeA.FirstIndex))); } if (RangeA.LastIndex > j) { exA.Add(new ItemIndexRange(j + 1, (uint)(RangeA.LastIndex - j))); } if (RangeB.FirstIndex < i) { exB.Add(new ItemIndexRange(RangeB.FirstIndex, (uint)(i - RangeB.FirstIndex))); } if (RangeB.LastIndex > j) { exB.Add(new ItemIndexRange(j + 1, (uint)(RangeB.LastIndex - j))); } OnlyInRangeA = exA.ToArray(); OnlyInRangeB = exB.ToArray(); return(true); } else { InBothAandB = default; OnlyInRangeA = new ItemIndexRange[] { RangeA }; OnlyInRangeB = new ItemIndexRange[] { RangeB }; return(false); } }
/// <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 }
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList<ItemIndexRange> trackedItems) { Weight = (double)LastRange.Length / (double)LastFetch.Length; int _firstDelta = visibleRange.FirstIndex - this.LastRange.FirstIndex; int _lastDelta = visibleRange.LastIndex - this.LastRange.LastIndex; Task.Run(() => { if (trackedItems != null) foreach (var item in trackedItems) { this.FetchFinal(item.FirstIndex, item.LastIndex); } }); this.Difference += (_firstDelta + _lastDelta) / (double)this.LastFetch.Length; if (this.Difference > Weight || this.LastFetch.FirstIndex > visibleRange.FirstIndex || this.LastFetch.LastIndex < visibleRange.LastIndex) { this.Difference = 0.0; this.PreScrollFactor = (_firstDelta + _lastDelta) / (double)visibleRange.Length; int _count = (int)visibleRange.Length * 2 + 3; int _offset = (int)((_count - (int)visibleRange.Length) * PreScrollFactor); int _first = (visibleRange.FirstIndex - _count / 2 + _offset); int _last = (visibleRange.LastIndex + _count / 2 + _offset); this.FetchFinal(_first, _last); // NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, , _albumList.GetRange(LastFetch.FirstIndex,(int)LastFetch.Length)); // this.OnCollectionChanged(e); } LastRange = visibleRange; }
public AlbumList() { LastRange = new ItemIndexRange(0, 0); }
// Called when an item (or items) are deselected public void DeselectRange(ItemIndexRange range) { selection.Subtract(range); selectionCache.UpdateRanges(selection.ToArray()); }
// Called when an item (or items) are selected public void SelectRange(ItemIndexRange range) { selection.Add(range); selectionCache.UpdateRanges(selection.ToArray()); }
async Task AddNewSendMessageToThreadBeforeEncryption(Message message) { await this._messageThreadView.OnMessageAddedAsync(message); this._currentItemIndexRange = new ItemIndexRange(this._currentItemIndexRange.FirstIndex, this._currentItemIndexRange.Length + 1); }
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList <ItemIndexRange> trackedItems) { VisibleItems = visibleRange; VisibleItemsChanged?.Invoke(this, visibleRange); }
static public string AsString(this ItemIndexRange me) { return(String.Format("[{0},{1}]", me.FirstIndex, me.LastIndex)); }
static public IEnumerable <ItemIndexRange> Merge(this IEnumerable <ItemIndexRange> ranges, ItemIndexRange range) { var sorted = ranges.Concat(new[] { range }).OrderByDescending(r => r.Length).OrderBy(r => r.FirstIndex); foreach (var item in MergeInternal(sorted.Skip(1), sorted.First())) { yield return(item); } }
private IList FetchFinal(int _first, int _last) { if (_first < 0) _first = 0; if (_last > _albumList.Count - 1) _last = _albumList.Count - 1; for (int i = _first; i <= _last; i++) { if (_albumList[i].IsFetched) { continue; } _albumList[i].Fetch(); } if (LastFetch != null) { var t = Task.Factory.StartNew(() => { for (int i = 0; i < _first; i++) { _albumList[i].IsFetched = false; _albumList[i].Collect(); } GC.Collect(); }); t.ContinueWith((task) => { for (int i = _albumList.Count - 1; i > _last; i--) { if (_albumList[i].IsFetched == false) continue; _albumList[i].IsFetched = false; _albumList[i].Collect(); } GC.Collect(); }); } LastFetch = new ItemIndexRange(_first, (uint)(_last - _first + 1)); return _albumList.GetRange(_first, (_last - _first + 1)); }
public static bool Intersects(this ItemIndexRange This, ItemIndexRange range) { return(range.FirstIndex >= This.FirstIndex && range.FirstIndex <= This.LastIndex || range.LastIndex >= This.FirstIndex && range.LastIndex <= This.LastIndex); }
public static bool ContiguousOrOverlaps(this ItemIndexRange itemIndexRange, ItemIndexRange range) { return (range.FirstIndex >= itemIndexRange.FirstIndex && range.FirstIndex <= itemIndexRange.LastIndex + 1) || (range.LastIndex + 1 >= itemIndexRange.FirstIndex && range.LastIndex <= itemIndexRange.LastIndex); }
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList <ItemIndexRange> trackedItems) { FetchRanges(trackedItems.Normalize().ToArray()); }
public static bool Equals(this ItemIndexRange itemIndexRange, ItemIndexRange range) { return itemIndexRange.FirstIndex == range.FirstIndex && itemIndexRange.Length == range.Length; }
public void SelectRange(ItemIndexRange itemIndexRange) { _rangeSelection = _rangeSelection.Merge(itemIndexRange); }
public static bool Intersects(this ItemIndexRange itemIndexRange, ItemIndexRange range) { return (range.FirstIndex >= itemIndexRange.FirstIndex && range.FirstIndex <= itemIndexRange.LastIndex) || (range.LastIndex >= itemIndexRange.FirstIndex && range.LastIndex <= itemIndexRange.LastIndex); }
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList <ItemIndexRange> trackedItems) { throw new NotImplementedException(); }
// Callback from the selection cache manager // Retrieves the keys for selected items private async Task<string[]> fetchSelectionDataCallback(ItemIndexRange batch, CancellationToken ct) { #if TRACE_DATASOURCE Debug.WriteLine("# SelectionDataCallback: " + batch.FirstIndex + "->" + batch.LastIndex); #endif // See if we already have the item in the data cache, if so get the key from there so we don't need to go to the filesystem var file = itemCache[batch.FirstIndex]; if (file != null) { return new string[] { file.Key }; } // Go get the keys from the file system if necessary IReadOnlyList<StorageFile> results = await _queryResult.GetFilesAsync((uint)batch.FirstIndex, batch.Length).AsTask(ct); List<string> keys = new List<string>(); if (results != null) { for (int i = 0; i < results.Count; i++) { ct.ThrowIfCancellationRequested(); keys.Add(results[i].FolderRelativeId); } } return keys.ToArray(); }
static public IEnumerable <ItemIndexRange> Subtract(this IEnumerable <ItemIndexRange> ranges, ItemIndexRange range) { if (ranges.Any()) { foreach (var r in ranges) { foreach (var item in Subtract(r, range)) { yield return(item); } } } }
/// <summary> /// Primary method for IItemsRangeInfo interface /// Is called when the list control's view is changed /// </summary> /// <param name="visibleRange">The range of items that are actually visible</param> /// <param name="trackedItems">Additional set of ranges that the list is using, for example the buffer regions and focussed element</param> public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList<ItemIndexRange> trackedItems) { #if TRACE_DATASOURCE string s = string.Format("* RangesChanged fired: Visible {0}->{1}", visibleRange.FirstIndex, visibleRange.LastIndex); foreach (ItemIndexRange r in trackedItems) { s += string.Format(" {0}->{1}", r.FirstIndex, r.LastIndex); } Debug.WriteLine(s); #endif // We know that the visible range is included in the broader range so don't need to hand it to the UpdateRanges call // Update the cache of items based on the new set of ranges. It will callback for additional data if required itemCache.UpdateRanges(trackedItems.ToArray()); }
static public bool Contains(this ItemIndexRange me, ItemIndexRange range) { return(me.FirstIndex <= range.FirstIndex && me.LastIndex >= range.LastIndex); }
// Callback from itemcache that it needs items to be retrieved private async Task<FileItem[]> fetchDataCallback(ItemIndexRange batch, CancellationToken ct) { // Fetch file objects from filesystem IReadOnlyList<StorageFile> results = await _queryResult.GetFilesAsync((uint)batch.FirstIndex, Math.Max(batch.Length, 20)).AsTask(ct); List<FileItem> files = new List<FileItem>(); if (results != null) { for (int i = 0; i < results.Count; i++) { ct.ThrowIfCancellationRequested(); // Create our FileItem object with the file data and thumbnail FileItem newItem = await FileItem.fromStorageFile(results[i], ct); files.Add(newItem); } } return files.ToArray(); }
static public bool Intersects(this ItemIndexRange me, int firstIndex, uint Length) { int LastIndex = firstIndex + (int)Length - 1; return((firstIndex >= me.FirstIndex && firstIndex <= me.LastIndex) || (LastIndex >= me.FirstIndex && LastIndex <= me.LastIndex)); }
static public IList <ItemIndexRange> Merge(this IList <ItemIndexRange> ranges, ItemIndexRange range) { return(Merge((IEnumerable <ItemIndexRange>)ranges, range).ToList()); }
static private IEnumerable <ItemIndexRange> MergeInternal(this IEnumerable <ItemIndexRange> ranges, ItemIndexRange range) { if (ranges.Any()) { var merge = Merge(ranges.First(), range).ToArray(); if (merge.Length == 2) { yield return(merge[0]); } range = merge.Last(); foreach (var item in MergeInternal(ranges.Skip(1), range)) { yield return(item); } yield break; } yield return(range); }
// Called by the timer to make a request for data public async void FetchData() { // Stop the timer so we don't get fired again unless data is requested _timer.Stop(); if (_requestInProgress != null) { // Verify if an active request is still needed if (_request.Intersects(_requestInProgress)) { return; } // Cancel the existing request _cancelTokenSource.Cancel(); } ItemIndexRange nextRequest = GetFirstRequestBlock(_maxBatchFetchSize); if (nextRequest != null) { _cancelTokenSource = new CancellationTokenSource(); CancellationToken ct = _cancelTokenSource.Token; _requestInProgress = nextRequest; T[] data = null; try { // Use the callback to get the data, passing in a cancellation token data = await _fetchDataCallback(nextRequest, ct); if (!ct.IsCancellationRequested) { for (int i = 0; i < data.Length; i++) { int cacheIndex = nextRequest.FirstIndex + i; T oldItem = this[cacheIndex]; T newItem = data[i]; if (!newItem.Equals(oldItem)) { this[cacheIndex] = newItem; // Fire CacheChanged so that the datasource can fire its INCC event, and do other work based on the item having data #pragma warning disable SA1118 // Parameter must not span multiple lines CacheChanged?.Invoke(this, new CacheChangedEventArgs <T> { OldItem = oldItem, NewItem = newItem, ItemIndex = cacheIndex }); #pragma warning restore SA1118 // Parameter must not span multiple lines } } _request.Subtract(new ItemIndexRange(nextRequest.FirstIndex, (uint)data.Length)); } } // Try/Catch is needed as cancellation is via an exception catch (OperationCanceledException) { } finally { _requestInProgress = null; // Start another request if required FetchData(); } } }
// Called by the timer to make a request for data public async void fetchData() { //Stop the timer so we don't get fired again unless data is requested timer.Stop(); if (this.requestInProgress != null) { // Verify if an active request is still needed if (this.requests.Intersects(requestInProgress)) { return; } else { // Cancel the existing request #if TRACE_DATASOURCE Debug.WriteLine(">" + debugName + " Cancelling request: " + requestInProgress.FirstIndex + "->" + requestInProgress.LastIndex); #endif cancelTokenSource.Cancel(); } } ItemIndexRange nextRequest = GetFirstRequestBlock(maxBatchFetchSize); if (nextRequest != null) { cancelTokenSource = new CancellationTokenSource(); CancellationToken ct = cancelTokenSource.Token; requestInProgress = nextRequest; T[] data = null; try { #if TRACE_DATASOURCE Debug.WriteLine(">" + debugName + " Fetching items " + nextRequest.FirstIndex + "->" + nextRequest.LastIndex); #endif // Use the callback to get the data, passing in a cancellation token data = await fetchDataCallback(nextRequest, ct); if (!ct.IsCancellationRequested) { #if TRACE_DATASOURCE Debug.WriteLine(">" + debugName + " Inserting items into cache at: " + nextRequest.FirstIndex + "->" + (nextRequest.FirstIndex + data.Length - 1)); #endif for (int i = 0; i < data.Length; i++) { int cacheIndex = (int)(nextRequest.FirstIndex + i); T oldItem = this[cacheIndex]; T newItem = data[i]; if (!newItem.Equals(oldItem)) { this[cacheIndex] = newItem; // Fire CacheChanged so that the datasource can fire its INCC event, and do other work based on the item having data if (CacheChanged != null) { CacheChanged(this, new CacheChangedEventArgs <T>() { oldItem = oldItem, newItem = newItem, itemIndex = cacheIndex }); } } } requests.Subtract(new ItemIndexRange(nextRequest.FirstIndex, (uint)data.Length)); } } // Try/Catch is needed as cancellation is via an exception catch (OperationCanceledException) { } finally { requestInProgress = null; // Start another request if required fetchData(); } } }
public static bool ContiguousOrOverlaps(this ItemIndexRange itemIndexRange, ItemIndexRange range) { return((range.FirstIndex >= itemIndexRange.FirstIndex && range.FirstIndex <= itemIndexRange.LastIndex + 1) || (range.LastIndex + 1 >= itemIndexRange.FirstIndex && range.LastIndex <= itemIndexRange.LastIndex)); }
public static bool Intersects(this ItemIndexRange This, int FirstIndex, uint Length) { int LastIndex = FirstIndex + (int)Length - 1; return(FirstIndex >= This.FirstIndex && FirstIndex <= This.LastIndex || LastIndex >= This.FirstIndex && LastIndex <= This.LastIndex); }
public static bool Intersects(this ItemIndexRange itemIndexRange, int firstIndex, uint length) { int lastIndex = firstIndex + (int)length - 1; return((firstIndex >= itemIndexRange.FirstIndex && firstIndex <= itemIndexRange.LastIndex) || (lastIndex >= itemIndexRange.FirstIndex && lastIndex <= itemIndexRange.LastIndex)); }
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList <ItemIndexRange> trackedItems) { // We know that the visible range is included in the broader range so don't need to hand it to the UpdateRanges call // Update the cache of items based on the new set of ranges. It will callback for additional data if required _itemCache.UpdateRanges(trackedItems.ToArray()); }
public static bool Equals(this ItemIndexRange itemIndexRange, ItemIndexRange range) { return(itemIndexRange.FirstIndex == range.FirstIndex && itemIndexRange.Length == range.Length); }
public static bool Equals(this ItemIndexRange This, ItemIndexRange range) { return(This.FirstIndex == range.FirstIndex && This.Length == range.Length); }
public static bool Equals(this ItemIndexRange This, ItemIndexRange range) { return (This.FirstIndex == range.FirstIndex && This.Length == range.Length); }