protected internal override void OnQueryItems(VirtualPage page, AsyncQueryInfo queryInfo) { base.OnQueryItems(page, queryInfo); DataGridVirtualizingQueryableCollectionViewGroup collectionViewGroup = this.GetLinkedCollectionViewGroup(page.ParentVirtualList) as DataGridVirtualizingQueryableCollectionViewGroup; IQueryable queryableToUse; int virtualItemCount = collectionViewGroup.VirtualItemCount; bool queryableIsReversed; if ((!m_supportsPrimaryKeyOptimizations) || (queryInfo.StartIndex < (virtualItemCount / 2))) { queryableIsReversed = false; queryableToUse = collectionViewGroup.Queryable.Slice(queryInfo.StartIndex, queryInfo.RequestedItemCount); } else { queryableIsReversed = true; int reversedStartIndex = virtualItemCount - (queryInfo.StartIndex + queryInfo.RequestedItemCount); queryableToUse = collectionViewGroup.ReversedQueryable.Slice(reversedStartIndex, queryInfo.RequestedItemCount); } System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(this.AsyncGatherItems), new object[] { queryInfo, queryableToUse, queryableIsReversed }); }
protected internal override void OnQueryItemsCompleted(VirtualPage page, AsyncQueryInfo queryInfo, object[] fetchedItems) { base.OnQueryItemsCompleted(page, queryInfo, fetchedItems); Debug.Assert(m_asyncQueryInfosInProgress.Contains(queryInfo)); m_asyncQueryInfosInProgress.Remove(queryInfo); this.UpdateConnectionState(); }
private void AsyncQueryInfo_BeginQueryItems(AsyncQueryInfo queryInfo) { if (this.IsDisposed) { return; } Debug.Assert((this.ParentVirtualList != null) || (this.ParentVirtualList.PagingManager != null)); this.ParentVirtualList.PagingManager.OnQueryItems(this, queryInfo); }
private void AsyncQueryInfo_BuiltInAbort(AsyncQueryInfo queryInfo) { if (this.IsDisposed) { return; } Debug.Assert((this.ParentVirtualList != null) || (this.ParentVirtualList.PagingManager != null)); this.ParentVirtualList.PagingManager.OnBuiltInAbort(this, queryInfo); this.IsAborting = false; }
protected internal override void OnQueryItemsCompleted(VirtualPage page, AsyncQueryInfo queryInfo, object[] fetchedItems) { DataGridVirtualizingCollectionView collectionView = this.CollectionView as DataGridVirtualizingCollectionView; // The VirtualPageManager was Disposed if (collectionView == null) { return; } using (collectionView.DeferRefresh()) { base.OnQueryItemsCompleted(page, queryInfo, fetchedItems); } }
protected internal override void OnAbortQueryItems(VirtualPage page, AsyncQueryInfo queryInfo) { base.OnAbortQueryItems(page, queryInfo); // It is possible that the queryInfo was removed previously m_asyncQueryInfosInProgress.Remove(queryInfo); // In case the page query was aborted when // VirtualPageManager.CleanUpUnused is called if (page.RemoveAfterOperation) { this.RemovePage(page); } this.UpdateConnectionState(); }
internal void OnQueryItems(AsyncQueryInfo asyncQueryInfo, DataGridVirtualizingCollectionViewGroup collectionViewGroup) { QueryItemsEventArgs e = new QueryItemsEventArgs(this, collectionViewGroup, asyncQueryInfo); if (this.QueryItems != null) { this.QueryItems(this, e); } DataGridVirtualizingCollectionViewSource source = this.ParentCollectionViewSourceBase as DataGridVirtualizingCollectionViewSource; if (source != null) { source.OnQueryItems(e); } }
private void AsyncQueryInfo_EndQueryItems(AsyncQueryInfo queryInfo, object[] fetchedItems) { if (this.IsDisposed) { return; } Debug.Assert((this.ParentVirtualList != null) || (this.ParentVirtualList.PagingManager != null)); Debug.Assert(!this.IsAborting); this.ParentVirtualList.PagingManager.OnQueryItemsCompleted(this, queryInfo, fetchedItems); if (this.ParentVirtualList.IsRestarting) { this.Restart(); } }
private void AsyncQueryInfo_QueryErrorChanged(AsyncQueryInfo queryInfo) { if (this.IsDisposed) { return; } Debug.Assert((this.ParentVirtualList != null) || (this.ParentVirtualList.PagingManager != null)); this.ParentVirtualList.PagingManager.OnQueryErrorChanged(this, queryInfo); if (this.ParentVirtualList.IsRestarting) { this.Restart(); } }
protected internal override void OnAbortQueryItems(VirtualPage page, AsyncQueryInfo queryInfo) { DataGridVirtualizingCollectionView collectionView = this.CollectionView as DataGridVirtualizingCollectionView; // The VirtualPageManager was Disposed if (collectionView == null) { return; } DataGridVirtualizingCollectionViewGroup collectionViewGroup = this.GetLinkedCollectionViewGroup(page.ParentVirtualList) as DataGridVirtualizingCollectionViewGroup; collectionView.OnAbortQueryItems(queryInfo, collectionViewGroup); base.OnAbortQueryItems(page, queryInfo); }
protected internal override void OnBuiltInAbort(VirtualPage page, AsyncQueryInfo queryInfo) { // When a built-in abort occurs, we ensure to remove // any AsyncQueryInfo from references since the ConnectionState // use this array to update its actual state m_asyncQueryInfosInProgress.Remove(queryInfo); // In case the page query was aborted when // VirtualPageManager.CleanUpUnused is called if (page.RemoveAfterOperation) { this.RemovePage(page); } this.UpdateConnectionState(); }
internal QueryItemsEventArgs( DataGridVirtualizingCollectionView collectionView, DataGridVirtualizingCollectionViewGroup collectionViewGroup, AsyncQueryInfo asyncQueryInfo) { m_dataGridVirtualizingCollectionView = collectionView; // The collectionViewGroup can be null when we abort // a QueryItems for the old RootGroup when // DataGridVirtualizingCollectionViewBase.ForceRefresh // is called m_readonlyGroupPath = (collectionViewGroup != null) ? collectionViewGroup.GroupPath.AsReadOnly() : new ReadOnlyCollection <DataGridGroupInfo>(new List <DataGridGroupInfo>()); m_asyncQueryInfo = asyncQueryInfo; }
internal void QueueQueryData(Dispatcher dispatcher) { Debug.Assert(!this.IsDisposed); Debug.Assert(m_asyncQueryInfo == null); m_asyncQueryInfo = new AsyncQueryInfo( dispatcher, new Action <AsyncQueryInfo>(this.AsyncQueryInfo_BeginQueryItems), new Action <AsyncQueryInfo>(this.AsyncQueryInfo_AbortQueryItems), new Action <AsyncQueryInfo, object[]>(this.AsyncQueryInfo_EndQueryItems), new Action <AsyncQueryInfo>(this.AsyncQueryInfo_QueryErrorChanged), new Action <AsyncQueryInfo>(this.AsyncQueryInfo_BuiltInAbort), m_startDataIndex, this.Count); m_asyncQueryInfo.QueueQuery(); }
public void Dispose() { Debug.Assert(!this.IsDisposed); Debug.Assert((m_asyncCommitInfoList != null) && (m_asyncCommitInfoList.Count == 0), "Some async commit are not completed while disposing VirtualPage"); if (m_asyncQueryInfo != null) { // We must dispose the AsyncQueryInfo to be sure // it does not root this VirtualPage instance m_asyncQueryInfo.Dispose(); m_asyncQueryInfo = null; } this.Clear(); m_parentVirtualList = null; this.IsDisposed = true; }
private void AsyncQueryInfo_AbortQueryItems(AsyncQueryInfo queryInfo) { if (this.IsDisposed) { return; } Debug.Assert((this.ParentVirtualList != null) || (this.ParentVirtualList.PagingManager != null)); this.ParentVirtualList.PagingManager.OnAbortQueryItems(this, queryInfo); this.IsAborting = false; // If the page was removed, it was also disposed. // This case means the page was not restarting if (!this.RemoveAfterOperation && this.ParentVirtualList.IsRestarting) { this.Restart(); } }
protected internal override void OnQueryErrorChanged(VirtualPage page, AsyncQueryInfo queryInfo) { base.OnQueryErrorChanged(page, queryInfo); // It is possible that m_asyncQueryInfosInProgress does not contain the queryInfo when // the query was aborted but the user did not stop the query and later on set the queryInfo error // event if the queryInfo ShouldAbort is set to True. Debug.Assert((m_asyncQueryInfosInProgress.Contains(queryInfo)) || (queryInfo.ShouldAbort)); object error = queryInfo.Error; if (error == null) { // Even if the queryInfo's ShouldAbort property is set to True, clean-up the error. Debug.Assert((m_asyncQueryInfosInError != null) && (m_asyncQueryInfosInError.Contains(queryInfo))); m_asyncQueryInfosInError.Remove(queryInfo); if (m_asyncQueryInfosInError.Count == 0) { m_asyncQueryInfosInError = null; } } else if (!queryInfo.ShouldAbort) { // Only add errors if the queryInfo's ShouldAbort property is set to False. if (m_asyncQueryInfosInError == null) { m_asyncQueryInfosInError = new LinkedList <AsyncQueryInfo>(); } if (m_asyncQueryInfosInError.Contains(queryInfo)) { m_asyncQueryInfosInError.Remove(queryInfo); } m_asyncQueryInfosInError.AddFirst(queryInfo); } this.UpdateConnectionState(); }
internal void EndQueryItems(AsyncQueryInfo asyncQueryInfo, object[] items) { Debug.Assert(!this.IsDisposed); // This can occur when the user notify us that the QueryData is completed for an AsyncQueryInfo.StartIndex that refers to a Page which does exists // but that was removed, then re-created thus creating another asyncQueryInfo and queuing another QueryData. // The only way to get rid of this situation would be to keep a ref to the queued asyncQueryInfo even if we get rid of the page and re-link the same instance // to the newly created page. This optimization could be done in a future version. For now, let's return and the second asyncQueryInfo will take care of filling // the newly created page. if (m_asyncQueryInfo != asyncQueryInfo) { Debug.Assert(false); return; } if (this.IsFilled) { throw new InvalidOperationException("An attempt was made to fill a virtual page that is already filled."); } if (items == null) { throw new ArgumentNullException("items"); } for (int i = 0; i < items.Length; i++) { VirtualizedItemInfo virtualizedItemInfo = this[i]; Debug.Assert(virtualizedItemInfo.DataItem is EmptyDataItem); Debug.Assert(virtualizedItemInfo.Index == (m_startDataIndex + i)); this[i].DataItem = items[i]; } this.IsFilled = true; Debug.WriteLineIf(VirtualPageManager.DebugDataVirtualization, "Page Filled - " + this.ToString()); }
protected internal override void OnQueryItems(VirtualPage page, AsyncQueryInfo queryInfo) { // The VirtualPageManager is not connected to the CollectionView anymore, // do NOT query items since it will be done by the new VirtualPageManager // assigned to the same CollectionView if (!this.IsConnected) { return; } Debug.Assert(!m_asyncQueryInfosInProgress.Contains(queryInfo)); m_asyncQueryInfosInProgress.Add(queryInfo); if (m_asyncQueryInfosInError != null) { LinkedListNode <AsyncQueryInfo> queryInfoInErrorNode = m_asyncQueryInfosInError.First; while (queryInfoInErrorNode != null) { if (DataGridPageManagerBase.QueryInfoWeakComparer.Equals(queryInfo, queryInfoInErrorNode.Value)) { m_asyncQueryInfosInError.Remove(queryInfoInErrorNode); break; } queryInfoInErrorNode = queryInfoInErrorNode.Next; } if (m_asyncQueryInfosInError.Count == 0) { m_asyncQueryInfosInError = null; } } this.UpdateConnectionState(); }
internal void FillEmptyPage(AsyncQueryInfo asyncQueryInfo, object[] fetchedItems) { // The VirtualList is disposed or part of a PagingManager // that will be disposed (only disconnected when dispose is required) if (!this.PagingManager.IsConnected) { return; } Debug.Assert(!this.IsDisposed); Debug.Assert(!asyncQueryInfo.IsDisposed); // We do not want to move the page we are about to fill to the front since it does not count as a legitimate user-acess. // It will get moved to the front when one of its item is accessed. VirtualPage page = null; page = this.GetPageOrDefaultForItemIndex(asyncQueryInfo.StartIndex, true); // Although extremely rare, this situation could occur if we are calling RemovePageNode and the QueryData Dispatcher Operation // which has been asyncronously invoked in CreateNewPage is raising the QueryData event at the exact moment when we // try to abort the dispatcher operation. This means that the customer will have queued an async request for data // for a page we no longer care about, and have already removed from the Table of Content and our LinkedList. // This should NOT occur if the user did not abort the request and called the AsyncQueryInfo EndQuery method since AsyncQueryInfo should // not have invoked the EndQueryAction if its ShouldAbort property was set to true. if (page == null) { return; } Debug.Assert(!page.IsFilled); Debug.Assert(this.GetPageStartingIndexForItemIndex(asyncQueryInfo.StartIndex) == asyncQueryInfo.StartIndex); Debug.Assert(fetchedItems.Length <= page.Count); if (fetchedItems.Length == page.Count) { object[] oldItems = page.ToItemArray(); m_tableOfContent.RemovePage(page); page.EndQueryItems(asyncQueryInfo, fetchedItems); m_tableOfContent.AddPage(page); Debug.WriteLineIf(VirtualPageManager.DebugDataVirtualization, "Replaced TOC items/index for page: " + page.ToString()); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, fetchedItems, oldItems, asyncQueryInfo.StartIndex)); } else { // The expected count was not met. Maybe the user told us the source was bigger than it really is, or maybe there // were delete operations made on the source since the last restart. // // Let's refresh the CollectionView. // This will restart the VirtualItemBook and raise the CollectionView's OnCollectionChanged Reset notification. this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } }
protected internal abstract void OnBuiltInAbort(VirtualPage virtualPage, AsyncQueryInfo queryInfo);
protected internal abstract void OnQueryItems(VirtualPage page, AsyncQueryInfo queryInfo);
protected internal virtual void OnAbortQueryItems(VirtualPage page, AsyncQueryInfo queryInfo) { }
protected internal virtual void OnQueryItemsCompleted(VirtualPage page, AsyncQueryInfo queryInfo, object[] fetchedItems) { this.IncrementVersion(); page.ParentVirtualList.FillEmptyPage(queryInfo, fetchedItems); }
protected internal virtual void OnQueryErrorChanged(VirtualPage page, AsyncQueryInfo queryInfo) { }
private void AsyncGatherItems(object workItem) { object[] parameters = ( object[] )workItem; AsyncQueryInfo queryInfo = parameters[0] as AsyncQueryInfo; if (queryInfo.ShouldAbort) { return; } IQueryable queryable = ( IQueryable )parameters[1]; int requestedItemCount = queryInfo.RequestedItemCount; object[] items = new object[requestedItemCount]; System.Collections.IEnumerator enumerator; lock ( m_syncRoot ) { // We reverify here since a reset could have been issued while we were waiting on the lock statement. if ((queryInfo.ShouldAbort) || (!this.IsConnected) || (this.IsDisposed)) { return; } try { Debug.WriteLineIf(VirtualPageManager.DebugDataVirtualization, "Beginning Provider Execute for page at start index: " + queryInfo.StartIndex.ToString()); enumerator = queryable.GetEnumerator(); Debug.WriteLineIf(VirtualPageManager.DebugDataVirtualization, "Ended Provider Execute for page at start index: " + queryInfo.StartIndex.ToString()); int i = 0; while (enumerator.MoveNext() && (i < requestedItemCount)) { object current = enumerator.Current; if (current != null) { items[i] = enumerator.Current; i++; } } } catch (Exception exception) { // TimeOut exeception or other. queryInfo.AbortQuery(); queryInfo.Error = exception.Message; return; } } items = items.Where(item => item != null).ToArray(); try { if (items.Count() != requestedItemCount) { throw new InvalidOperationException("TODODOC : The number of non-null items return by the source must be equal to the provided item count"); } } catch { //go silently here, in case the next sequance give a valid result. At the same time, if it does not, the dev has information when debugging. } bool queryableWasReversed = ( bool )parameters[2]; if (queryableWasReversed) { Array.Reverse(items); } queryInfo.EndQuery(items); }