private void HandleDetailReset( object masterItem, DetailGeneratorNode detailNode ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); int masterIndex = nodeHelper.FindItem( masterItem ); // -1 means either taht the master item is below a collapsed group node, or that the item does not exists, validate. if( masterIndex == -1 ) { nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); if( !nodeHelper.Contains( masterItem ) ) throw new DataGridInternalException(); } ItemsGeneratorNode masterNode = nodeHelper.CurrentNode as ItemsGeneratorNode; Debug.Assert( masterNode != null, "masterNode != null" ); #if LOG Log.Assert( this, masterNode != null, "masterNode != null" ); #endif //start index will be ignored later on if the masterIndex is -1!! int startIndex = nodeHelper.Index + masterNode.IndexOf( masterItem ) + 1; //details start a master index + 1 List<DetailGeneratorNode> detailsForMaster = null; //edge case, it is possible to receive a Reset from Floating details! if( masterNode.Details == null ) { //check for floating details, if not present, throw, this is an abnormal case. if( !m_floatingDetails.Contains( masterItem ) ) { throw new DataGridInternalException(); } } else { masterNode.Details.TryGetValue( masterNode.Items.IndexOf( masterItem ), out detailsForMaster ); Debug.Assert( detailsForMaster != null, "detailsForMaster != null" ); #if LOG Log.Assert( this, detailsForMaster != null, "detailsForMaster != null" ); #endif } if( detailsForMaster != null ) { //this is required to ensure that if the details that resets is not the first one, the index is calculated appropriatly. foreach( DetailGeneratorNode node in detailsForMaster ) { if( node == detailNode ) { break; } else { startIndex += node.ItemCount; } } //if there were 'items' in the detail node, process the remove of them int oldDetailCount = detailNode.ItemCount; if( oldDetailCount > 0 ) { int endIndex = startIndex + oldDetailCount - 1; //last detail index GeneratorPosition removeGenPos = ( masterIndex != -1 ) ? this.GeneratorPositionFromIndex( startIndex ) : new GeneratorPosition( -1, 1 ); int genRemCount = 0; List<DependencyObject> removedContainers = new List<DependencyObject>(); //this has no uses if the masterIndex is -1 ( collapsed master item ) if( masterIndex != -1 ) { genRemCount = this.RemoveGeneratedItems( startIndex, endIndex, removedContainers ); } masterNode.AdjustItemCount( -oldDetailCount ); this.IncrementCurrentGenerationCount(); //this has no uses if the masterIndex is -1 ( collapsed master item ) if( masterIndex != -1 ) { this.SendRemoveEvent( removeGenPos, masterIndex + 1, oldDetailCount, genRemCount, removedContainers ); } } detailNode.UpdateItemCount(); int newDetailCount = detailNode.ItemCount; if( newDetailCount > 0 ) { GeneratorPosition addGenPos = new GeneratorPosition( -1, 1 ); //this has no uses if the masterIndex is -1 ( collapsed master item ) if( masterIndex != -1 ) { addGenPos = this.GeneratorPositionFromIndex( startIndex ); } masterNode.AdjustItemCount( newDetailCount ); this.IncrementCurrentGenerationCount(); //this has no uses if the masterIndex is -1 ( collapsed master item ) if( masterIndex != -1 ) { this.SendAddEvent( addGenPos, masterIndex + 1, newDetailCount ); } } } }
private void HandleDetailMoveRemove( object masterItem, DetailGeneratorNode detailNode, CustomGeneratorChangedEventArgs e ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); int masterIndex = nodeHelper.FindItem( masterItem ); //If the masterItem is part of an ItemsGeneratorNode which is below a collapsed group, masterIndex will be -1 if( masterIndex == -1 ) { //in that case, I need to determine the appropriate masterNode another way nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); if( !nodeHelper.Contains( masterItem ) ) throw new DataGridInternalException(); } ItemsGeneratorNode masterNode = nodeHelper.CurrentNode as ItemsGeneratorNode; Debug.Assert( masterNode != null, "masterNode != null" ); #if LOG Log.Assert( this, masterNode != null, "masterNode != null" ); #endif int globalIndex = -1; GeneratorPosition convertedGeneratorPosition = ( masterIndex != -1 ) ? this.ConvertDetailGeneratorPosition( e.OldPosition, masterItem, detailNode, out globalIndex ) : new GeneratorPosition( -1, 1 ); if( masterIndex != -1 ) { this.RemoveDetailContainers( convertedGeneratorPosition, e.ItemUICount ); } if( e.Action == NotifyCollectionChangedAction.Remove ) { masterNode.AdjustItemCount( -e.ItemCount ); detailNode.UpdateItemCount(); } this.IncrementCurrentGenerationCount(); if( masterIndex != -1 ) { this.SendRemoveEvent( convertedGeneratorPosition, globalIndex, e.ItemCount, e.ItemUICount, e.RemovedContainers ); } }
private int FindGlobalIndexForDetailNode( DetailGeneratorNode detailNode ) { int retval = -1; //first thing, loop through the details for the foreach( KeyValuePair<object, List<DetailGeneratorNode>> masterItemToDetails in m_masterToDetails ) { //in each master to details entry, try to find the detail node passed int detailIndex = masterItemToDetails.Value.IndexOf( detailNode ); if( detailIndex != -1 ) { //if the desired detailNode is present for this master item... then evaluate it... int detailNodeOffset = 0; for( int i = 0; i < detailIndex; i++ ) { detailNodeOffset += masterItemToDetails.Value[ i ].ItemCount - 1; } int index = m_genPosToItem.IndexOf( masterItemToDetails.Key ); int masterItemIndex; if( index > -1 ) { masterItemIndex = m_genPosToIndex[ index ]; } else { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); masterItemIndex = nodeHelper.FindItem( masterItemToDetails.Key ); } if( masterItemIndex == -1 ) throw new DataGridInternalException(); retval = detailNodeOffset + masterItemIndex + detailIndex + 1; //leave the top level loop break; } } return retval; }
private void UpdateGenPosToIndexList() { #if LOG int count = m_genPosToIndex.Count; int previousIndex = -1; for( int i = 0; i < count; i++ ) { int tempIndex = m_genPosToIndex[ i ]; if( tempIndex >= previousIndex ) { previousIndex = tempIndex; } else { Debug.Assert( false, "### none sequential index detected (before)" ); Log.Assert( this, false, "### none sequential index detected (before)" ); this.WriteStateInLog(); break; } } #endif //after the modification to have the item count stored "locally" in the DetailGeneratorNodes, //it becomes important to have the nodes updated when the layout of the items changes. if( m_masterToDetails.Count > 0 ) { foreach( KeyValuePair<object, List<DetailGeneratorNode>> masterToDetails in m_masterToDetails ) { foreach( DetailGeneratorNode detailNode in masterToDetails.Value ) { detailNode.UpdateItemCount(); } } } if( m_startNode != null ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); //index is 0, since i'm beginning at the start //(and I want to reindex the whole list) DetailGeneratorNode cachedDetailNode = null; int cachedDetailNodeIndex = -1; int previousDetailIndex = -1; //loop through the realized items. By design they are present sequentially in the linked list, so I do not need to reset the GeneratorNodeHelper for( int i = 0; i < m_genPosToItem.Count; i++ ) { object item = m_genPosToItem[ i ]; DetailGeneratorNode detailNode = m_genPosToNode[ i ] as DetailGeneratorNode; if( detailNode == null ) { //find the Item int tmpIndex = nodeHelper.FindItem( item ); if( tmpIndex != -1 ) { //set the Index (new) for the item ItemsGeneratorNode itemsNode = nodeHelper.CurrentNode as ItemsGeneratorNode; if( itemsNode != null ) { tmpIndex = nodeHelper.Index + itemsNode.IndexOf( item ); } m_genPosToIndex[ i ] = tmpIndex; } else { #if LOG if( item == null ) { Log.WriteLine( this, "# UpdateGenPosToIndexList - item not found Dnull" ); } else { Log.WriteLine( this, "# UpdateGenPosToIndexList - Item not found D" + item.GetHashCode() ); } #endif //a possible fix for this is to set the index of the "not found" element to the same index as previous item in the generated list... m_genPosToIndex[ i ] = ( i > 0 ) ? m_genPosToIndex[ i - 1 ] : 0; //item is not in the linked list... there is a problem, throw. //throw new DataGridInternalException(); } } else //else is detailNode != null { if( cachedDetailNode != detailNode ) { cachedDetailNodeIndex = this.FindGlobalIndexForDetailNode( detailNode ); cachedDetailNode = detailNode; previousDetailIndex = -1; } int detailIndex = detailNode.DetailGenerator.IndexFromRealizedItem( item, previousDetailIndex + 1, out previousDetailIndex ); m_genPosToIndex[ i ] = cachedDetailNodeIndex + detailIndex; } } //end for() } #if LOG count = m_genPosToIndex.Count; previousIndex = -1; for( int i = 0; i < count; i++ ) { int tempIndex = m_genPosToIndex[ i ]; if( tempIndex >= previousIndex ) { previousIndex = tempIndex; } else { Debug.Assert( false, "### none sequential index detected (after)" ); Log.Assert( this, false, "### none sequential index detected (after)" ); this.WriteStateInLog(); break; } } #endif }
public int IndexFromItem( object item ) { int retval = -1; if( item != null ) { this.EnsureNodeTreeCreated(); if( m_startNode != null ) { //if item is generated int generatedIndex = this.FindFirstGeneratedIndexForLocalItem( item ); if( generatedIndex != -1 ) { retval = m_genPosToIndex[ generatedIndex ]; } //if the item is not generated else { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); //Note: Under that specific case, I do not want to search through collapsed group nodes... Effectivelly, an item "below" a collapsed group node // Have no index as per the "item to index" interface of the generator. retval = nodeHelper.FindItem( item ); } } } return retval; }
public List<StickyContainerGenerated> GenerateStickyFooters( DependencyObject container, bool areFootersSticky, bool areGroupFootersSticky ) { List<StickyContainerGenerated> generatedStickyContainers = new List<StickyContainerGenerated>(); GeneratorNode containerNode; int containerRealizedIndex; object containerDataItem; if( this.FindGeneratorListMappingInformationForContainer( container, out containerNode, out containerRealizedIndex, out containerDataItem ) ) { GeneratorNodeHelper nodeHelper = null; DetailGeneratorNode detailNode = containerNode as DetailGeneratorNode; if( detailNode != null ) { // Get the parent item of the detail node to be able // to readjust the containerRealizedIndex to the one // of the master item container since the detail node // will be processed by the DetailGenerator. containerDataItem = detailNode.DetailContext.ParentItem; // OPTIMIZATION: We will look in the m_genPos* first to avoid using // FindItem for performance reason. int index = m_genPosToItem.IndexOf( containerDataItem ); if( index > -1 ) { int sourceDataIndex = ( int )m_genPosToContainer[ index ].GetValue( DataGridVirtualizingPanel.ItemIndexProperty ); containerNode = m_genPosToNode[ index ]; containerRealizedIndex = m_genPosToIndex[ index ]; CollectionGeneratorNode collectionNode = containerNode as CollectionGeneratorNode; if( collectionNode != null ) { nodeHelper = new GeneratorNodeHelper( containerNode, containerRealizedIndex - collectionNode.IndexOf( containerDataItem ), sourceDataIndex ); } } if( nodeHelper == null ) { // We want to find the ItemsGeneratorNode for the DetailNode. nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); containerRealizedIndex = nodeHelper.FindItem( containerDataItem ); containerNode = nodeHelper.CurrentNode; } if( containerRealizedIndex == -1 ) throw new DataGridInternalException(); generatedStickyContainers.AddRange( this.GenerateStickyFootersForDetail( container, detailNode, areFootersSticky, areGroupFootersSticky ) ); } else { CollectionGeneratorNode collectionNode = containerNode as CollectionGeneratorNode; if( collectionNode != null ) { // We don't need to have an up to date sourceDataIndex so we pass 0 nodeHelper = new GeneratorNodeHelper( containerNode, containerRealizedIndex - collectionNode.IndexOf( containerDataItem ), 0 ); } if( nodeHelper == null ) { nodeHelper = new GeneratorNodeHelper( containerNode, 0, 0 ); nodeHelper.ReverseCalculateIndex(); } } bool isHeaderNode = ( ( nodeHelper.CurrentNode is HeadersFootersGeneratorNode ) && ( nodeHelper.CurrentNode.Previous == null ) ); // We want to find the HeaderFooterGeneratorNode for the container // node. This is to find the footers for the container. nodeHelper.MoveToEnd(); HeadersFootersGeneratorNode footersNode = nodeHelper.CurrentNode as HeadersFootersGeneratorNode; if( !isHeaderNode ) { // There is no footers to generate if the item count of the node is 0. if( footersNode.ItemCount > 0 ) { if( ( ( areFootersSticky ) && ( footersNode.Parent == null ) ) || ( ( areGroupFootersSticky ) && ( footersNode.Parent is GroupGeneratorNode ) ) ) { generatedStickyContainers.AddRange( this.GenerateStickyFootersForNode( footersNode, nodeHelper.Index, containerRealizedIndex, ( footersNode == containerNode ) ) ); } } } // We must also find the bottom most footers for our level of detail and, if they need to be sticky, // we will generate the containers and add them the to list. HeadersFootersGeneratorNode bottomFootersNode = this.GetDetailFootersNode( nodeHelper ); if( ( areFootersSticky ) && ( bottomFootersNode != null ) && ( bottomFootersNode != footersNode ) && ( bottomFootersNode.ItemCount > 0 ) ) { generatedStickyContainers.AddRange( this.GenerateStickyFootersForNode( bottomFootersNode, nodeHelper.Index ) ); } } return generatedStickyContainers; }