internal bool InsertAfter(GeneratorNode insert) { if (insert == null) { throw new DataGridInternalException("GeneratorNode is null."); } var insertionCount = default(int); var chainLength = default(int); var insertLast = GeneratorNodeHelper.EvaluateChain(insert, out insertionCount, out chainLength); var nextNode = m_state.Node.Next; if (nextNode != null) { nextNode.Previous = insertLast; } insertLast.Next = nextNode; insert.Previous = m_state.Node; m_state.Node.Next = insert; // Move the current node to the last node inserted if (!this.MoveToNextBy(chainLength)) { throw new DataGridInternalException("Unable to move to the requested generator index."); } return(true); }
internal static GeneratorNode EvaluateChain(GeneratorNode startNode, out int totalChildCount, out int chainLength) { if (startNode == null) { throw new ArgumentNullException("startNode"); } var current = new State(startNode, 0, 0); totalChildCount = startNode.ItemCount; chainLength = 1; while (true) { if (!GeneratorNodeHelper.MoveToNext(ref current)) { break; } totalChildCount += current.Node.ItemCount; chainLength++; } return(current.Node); }
public bool InsertAfter(GeneratorNode insert) { if (insert == null) { throw new DataGridInternalException(); } int insertionCount; int chainLength; GeneratorNode insertLast = GeneratorNodeHelper.EvaluateChain(insert, out insertionCount, out chainLength); if (m_currentNode.Next != null) { m_currentNode.Next.Previous = insertLast; } insertLast.Next = m_currentNode.Next; insert.Previous = m_currentNode; m_currentNode.Next = insert; // Move the current node to the last node inserted if (!this.MoveToNextBy(chainLength)) { throw new DataGridInternalException(); } return(true); }
public bool MoveToFollowing() { bool retval = false; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper(m_currentNode, m_index, m_sourceDataIndex); while (!retval) { retval = nodeHelper.MoveToNext(); if (!retval) { if (!nodeHelper.MoveToParent()) { //cannot move to parent and could not move to next, this is the end of the chain. break; } } } if (retval) { m_currentNode = nodeHelper.CurrentNode; m_index = nodeHelper.Index; m_sourceDataIndex = nodeHelper.SourceDataIndex; } return(retval); }
//Note: this function will not check for the presence of the item in the details for Items nodes. internal bool AbsoluteFindItem(object item) { // This method will search through nodes, even those collapsed for the item. var current = m_state; while (true) { var itemsNode = current.Node as CollectionGeneratorNode; if ((itemsNode != null) && itemsNode.Items.Contains(item)) { break; } // If we reach this point, it's because the item we are looking // for is not in this node... Try to access the child if (!GeneratorNodeHelper.MoveToChild(ref current, false)) { // Try "advancing" to the next item. if (!GeneratorNodeHelper.MoveToFollowing(ref current)) { return(false); } } } m_state = current; this.EnsureState(); return(true); }
internal bool MoveBackward() { var startNode = m_state.Node; while (true) { if (!(m_state.Node is GroupGeneratorNode) && (m_state.Node != startNode) && (m_state.Node.ItemCount != 0)) { break; } if (!GeneratorNodeHelper.MoveToChild(ref m_state)) { if (!GeneratorNodeHelper.MoveToPreceding(ref m_state)) { return(false); } } else { GeneratorNodeHelper.MoveToEnd(ref m_state); } } this.EnsureState(); return(true); }
internal bool MoveToNextBy(int count) { var success = GeneratorNodeHelper.MoveToNext(ref m_state, count); this.EnsureState(); return(success); }
internal bool MoveToPrevious() { var success = GeneratorNodeHelper.MoveToPrevious(ref m_state); this.EnsureState(); return(success); }
internal bool MoveToChild(bool skipItemLessGroupNodes) { var success = GeneratorNodeHelper.MoveToChild(ref m_state, skipItemLessGroupNodes); this.EnsureState(); return(success); }
internal bool FindNodeForIndex(int index) { var success = GeneratorNodeHelper.FindNodeForIndex(ref m_state, index); this.EnsureState(); return(success); }
internal bool MoveToFirst() { GeneratorNodeHelper.MoveToFirst(ref m_state); this.EnsureState(); return(true); }
internal int FindItem(object item) { var current = m_state; while (true) { var itemsNode = current.Node as ItemsGeneratorNode; if (itemsNode != null) { var index = itemsNode.Items.IndexOf(item); if (index >= 0) { index += itemsNode.CountDetailsBeforeDataIndex(index); // Item is directly from this items node... then return the appropriate index! m_state = current; this.EnsureState(); return(index + current.Index); } // If the item is from a detail, then I don't want to "use" it!!! // but continue looping.... to find occurances of this item somewhere else in the tree. } else { var collectionNode = current.Node as CollectionGeneratorNode; if (collectionNode != null) { var index = collectionNode.IndexOf(item); if (index >= 0) { m_state = current; this.EnsureState(); return(index + current.Index); } } } // If we reach this point, it's because the item we are looking // for is not in this node... Try to access the child if (!GeneratorNodeHelper.MoveToChild(ref current)) { // Try "advancing" to the next item. if (!GeneratorNodeHelper.MoveToFollowing(ref current)) { break; } } } return(-1); }
internal bool MoveToEnd() { // We call MoveToNext instead of MoveToEnd because MoveToEnd includes the indexes of the last node. while (GeneratorNodeHelper.MoveToNext(ref m_state)) { } this.EnsureState(); return(true); }
private static bool MoveToFollowing(ref State state) { while (!GeneratorNodeHelper.MoveToNext(ref state)) { if (!GeneratorNodeHelper.MoveToParent(ref state)) { return(false); } } return(true); }
private static bool MoveToPreceding(ref State state) { while (!GeneratorNodeHelper.MoveToPrevious(ref state)) { if (!GeneratorNodeHelper.MoveToParent(ref state)) { return(false); } } return(true); }
private void NotifyImmediateChildren(bool value) { if (this.Child != null) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper(this.Child, 0, 0); //index not important nodeHelper.MoveToEnd(); do { nodeHelper.CurrentNode.NotifyExpansionStateChanged(value); }while(nodeHelper.MoveToPrevious() == true); } }
internal bool FindNode(GeneratorNode node) { if (node == null) { throw new ArgumentNullException("node"); } var success = GeneratorNodeHelper.FindNode(ref m_state, node); this.EnsureState(); return(success); }
//Note: this function will not check for the presence of the group in the details for Items nodes. internal bool FindGroup(CollectionViewGroup group) { if (group == null) { return(false); } var current = m_state; while (true) { var groupNode = current.Node as GroupGeneratorNode; if (groupNode != null) { if (groupNode.CollectionViewGroup == group) { break; } if (!groupNode.CollectionViewGroup.IsBottomLevel) { if (!GeneratorNodeHelper.MoveToChild(ref current, false)) { return(false); } } else { if (!GeneratorNodeHelper.MoveToFollowing(ref current)) { return(false); } } } else { // There is nothing under a non GroupGeneratorNode, try to move Next node in the list. if (!GeneratorNodeHelper.MoveToFollowing(ref current)) { return(false); } } } m_state = current; this.EnsureState(); return(true); }
private static bool MoveToParent(ref State state) { if (state.Node.Level == 0) { return(false); } GeneratorNodeHelper.MoveToFirst(ref state); state.Node = state.Node.Parent; Debug.Assert(state.Node != null); return(true); }
internal ReadOnlyCollection <Group> GetImmediateUIGroups(int generatorCurrentGeneration) { if ((m_cachedGeneratorCurrentGeneration != generatorCurrentGeneration) || (m_readOnlyImmediateUIGroups == null)) { Debug.WriteLineIf(((m_cachedGeneratorCurrentGeneration != generatorCurrentGeneration) && (m_readOnlyImmediateUIGroups == null)), "Creating Groups collection since generator version differs AND ReadOnlyCollection is null."); Debug.WriteLineIf(((m_cachedGeneratorCurrentGeneration != generatorCurrentGeneration) && (m_readOnlyImmediateUIGroups != null)), "Creating Groups collection since generator version differs."); Debug.WriteLineIf(((m_cachedGeneratorCurrentGeneration == generatorCurrentGeneration) && (m_readOnlyImmediateUIGroups == null)), "Creating Groups collection even if generator version is the same, since ReadOnlyCollection is null."); m_cachedGeneratorCurrentGeneration = generatorCurrentGeneration; // Ensure collections. if (m_immediateUIGroups == null) { Debug.Assert(m_readOnlyImmediateUIGroups == null); m_immediateUIGroups = new Collection <Group>(); m_readOnlyImmediateUIGroups = new ReadOnlyCollection <Group>(m_immediateUIGroups); } else { Debug.Assert(m_readOnlyImmediateUIGroups != null); m_immediateUIGroups.Clear(); } // Recalculate. GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper(this, 0, 0); //index is not important while (nodeHelper.MoveToNext()) { GroupGeneratorNode groupGeneratorNode = nodeHelper.CurrentNode as GroupGeneratorNode; if (groupGeneratorNode == null) { continue; } m_immediateUIGroups.Add(groupGeneratorNode.UIGroup); } } return(m_readOnlyImmediateUIGroups); }
public bool ReverseCalculateIndex() { // index need to be 0, as I will use the value from the index once I backtracked all the way to the root. GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper(this.CurrentNode, 0, 0); while ((nodeHelper.CurrentNode.Previous != null) || (nodeHelper.CurrentNode.Parent != null)) { if (!nodeHelper.MoveToPrevious()) { nodeHelper.MoveToParent(); } } m_index = Math.Abs(nodeHelper.Index); m_sourceDataIndex = Math.Abs(nodeHelper.SourceDataIndex); return(true); }
public static GeneratorNode EvaluateChain(GeneratorNode chainStart, out int totalChildCount, out int chainLength) { //if we insert a chain of nodes, this GeneratorNodeHelper will help us GeneratorNodeHelper newHelper = new GeneratorNodeHelper(chainStart, 0, 0); //first determine the total number of childs from this "node" totalChildCount = 0; chainLength = 0; do { totalChildCount += newHelper.CurrentNode.ItemCount; chainLength++; }while(newHelper.MoveToNext()); //then, since we moved at the end of the "chain" return(newHelper.CurrentNode); }
private static State FindNodeLocation(GeneratorNode node) { var state = new State(node, 0, 0); while (true) { GeneratorNodeHelper.MoveToFirst(ref state); if (!GeneratorNodeHelper.MoveToParent(ref state)) { break; } } Debug.Assert(state.Index <= 0); Debug.Assert(state.DataIndex <= 0); return(new State(node, -state.Index, -state.DataIndex)); }
private static bool FindNode(ref State state, GeneratorNode node) { if (state.Node == node) { return(true); } var from = state; var fromLevel = state.Node.Level; var to = new State(node, 0, 0); var toLevel = node.Level; while (from.Node != to.Node) { if (fromLevel > toLevel) { if (!GeneratorNodeHelper.MoveToParent(ref from)) { return(false); } fromLevel = from.Node.Level; } else { if (!GeneratorNodeHelper.MoveToPrevious(ref to)) { if (!GeneratorNodeHelper.MoveToParent(ref to)) { return(false); } toLevel = to.Node.Level; } } } state.Node = node; state.Index = from.Index - to.Index; state.DataIndex = from.DataIndex - to.DataIndex; return(true); }
public bool MoveBackward() { bool recurseGroup = true; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper(m_currentNode, m_index, m_sourceDataIndex); do { GroupGeneratorNode groupNode = nodeHelper.CurrentNode as GroupGeneratorNode; if ((groupNode == null) && (nodeHelper.CurrentNode != m_currentNode) && (nodeHelper.CurrentNode.ItemCount != 0)) { m_currentNode = nodeHelper.CurrentNode; m_index = nodeHelper.Index; m_sourceDataIndex = nodeHelper.SourceDataIndex; return(true); } if ((recurseGroup) && (nodeHelper.MoveToChild())) { nodeHelper.MoveToEnd(); continue; } recurseGroup = true; if (nodeHelper.MoveToPrevious()) { continue; } if (nodeHelper.MoveToParent()) { recurseGroup = false; continue; } break; }while(true); return(false); }
internal static HeadersFootersGeneratorNode GetSameLevelFirstHeaderNode( GroupGeneratorNode generatorNode ) { HeadersFootersGeneratorNode headerGeneratorNode = null; GroupGeneratorNode parentGroupGeneratorNode = generatorNode.Parent as GroupGeneratorNode; if( parentGroupGeneratorNode == null ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( generatorNode, 0, 0 ); //index is not important if( nodeHelper.MoveToFirst() ) headerGeneratorNode = nodeHelper.CurrentNode as HeadersFootersGeneratorNode; } else { headerGeneratorNode = parentGroupGeneratorNode.Child as HeadersFootersGeneratorNode; } return headerGeneratorNode; }
internal bool InsertBefore(GeneratorNode insert) { if (insert == null) { throw new DataGridInternalException("GeneratorNode is null"); } var insertionCount = default(int); var chainLength = default(int); var insertLast = GeneratorNodeHelper.EvaluateChain(insert, out insertionCount, out chainLength); var previousNode = m_state.Node.Previous; if (previousNode != null) { previousNode.Next = insert; } insert.Previous = previousNode; m_state.Node.Previous = insertLast; insertLast.Next = m_state.Node; var parentGroup = insert.Parent as GroupGeneratorNode; if (parentGroup != null) { if (parentGroup.Child == m_state.Node) { parentGroup.Child = insert; } } // Move the current to the first item inserted. // No need to update the indexes since they will still be with the correct value. m_state.Node = insert; this.EnsureState(); return(true); }
private static bool FindNodeForIndex(ref State state, int index) { var current = state; // The algo is a single drill-down... for optimized performance.. // It first tries to go horizontally in the tree and then drills-down if it can. while (true) { var itemCount = current.Node.ItemCount; // Verify if the current node contains ( directly or its children ) the required index if (index < current.Index + itemCount) { // A Group node is the only node that can have children is by definition empty... let's dig deeper. // If we cannot travel deeper, then this node is the closest we get. if (!GeneratorNodeHelper.MoveToChild(ref current)) { break; } } else if ((index == current.Index) && (itemCount != 0)) { break; } else { if (!GeneratorNodeHelper.MoveToNext(ref current)) { return(false); } } } state.Node = current.Node; state.Index = current.Index; state.DataIndex = current.DataIndex; return(true); }
internal static HeadersFootersGeneratorNode GetSameLevelFirstHeaderNode(GroupGeneratorNode generatorNode) { HeadersFootersGeneratorNode headerGeneratorNode = null; GroupGeneratorNode parentGroupGeneratorNode = generatorNode.Parent as GroupGeneratorNode; if (parentGroupGeneratorNode == null) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper(generatorNode, 0, 0); //index is not important if (nodeHelper.MoveToFirst()) { headerGeneratorNode = nodeHelper.CurrentNode as HeadersFootersGeneratorNode; } } else { headerGeneratorNode = parentGroupGeneratorNode.Child as HeadersFootersGeneratorNode; } return(headerGeneratorNode); }
public bool InsertBefore(GeneratorNode insert) { if (insert == null) { throw new DataGridInternalException(); } int insertionCount; int chainLength; GeneratorNode insertLast = GeneratorNodeHelper.EvaluateChain(insert, out insertionCount, out chainLength); GeneratorNode previous = m_currentNode.Previous; if (previous != null) { previous.Next = insert; } insert.Previous = previous; m_currentNode.Previous = insertLast; insertLast.Next = m_currentNode; GroupGeneratorNode parentGroup = insert.Parent as GroupGeneratorNode; if (parentGroup != null) { if (parentGroup.Child == m_currentNode) { parentGroup.Child = insert; } } // Move the current to the first item inserted. // No need to change m_index, m_sourceDataIndex since they will still be with the correct value. m_currentNode = insert; return(true); }
internal object FindIndex(int index) { var current = m_state; while (true) { if (index < current.Index + current.Node.ItemCount) { var itemsNode = current.Node as CollectionGeneratorNode; if (itemsNode != null) { m_state = current; this.EnsureState(); return(itemsNode.GetAt(index - current.Index)); } if (!GeneratorNodeHelper.MoveToChild(ref current)) { break; } } else { // If we reach this point, it's because the item we are looking for is not in this node... Try to access the child. // No need to check for childs, since the condition above would catch it (childs part of ItemCount). if (!GeneratorNodeHelper.MoveToFollowing(ref current)) { break; } } } return(null); }
private bool TryGetParentGroupFromItemHelper( object item, out CollectionViewGroup collectionViewGroup ) { //This helper method is used to simplify previous code flow of the TryGetParentGroupFromItem method. collectionViewGroup = null; //----------------------------------------------- //1 - First check is to see of the item is a CVG. //----------------------------------------------- CollectionViewGroup groupItem = item as CollectionViewGroup; if( groupItem != null ) { Group group = this.GetGroupFromCollectionViewGroup( groupItem ); if( group != null ) { GroupGeneratorNode groupGeneratorNode = group.GeneratorNode; if( groupGeneratorNode.Parent == null ) { //no parent for speficied item. return true; } //if the nodeHelper was able to locate the content, use the nodeHelper's CurrentNode as the node for the item. collectionViewGroup = ( ( GroupGeneratorNode )groupGeneratorNode.Parent ).CollectionViewGroup; return true; } //item is a CVG, but is not present in the generator! return false; } //----------------------------------------------- //2 - Second check is to see if the item is already in the generated list //----------------------------------------------- //item might be in the "generated" list... much quicker to find-out if it is! //note: if the item belongs to a detail, then it will be excluded from the "fast" algo. int itemGenPosIndex = this.FindFirstGeneratedIndexForLocalItem( item ); if( itemGenPosIndex != -1 ) { //item was generated and was not from a DetailGeneratorNode if( m_genPosToNode[ itemGenPosIndex ].Parent == null ) { //no parent for speficied item. return true; } collectionViewGroup = ( ( GroupGeneratorNode )m_genPosToNode[ itemGenPosIndex ].Parent ).CollectionViewGroup; return true; } //----------------------------------------------- //3 - Third check is to check of the item is a GroupHeaderFooterItem //----------------------------------------------- if( item.GetType() == typeof( GroupHeaderFooterItem ) ) { GroupHeaderFooterItem groupHeaderFooterItem = ( GroupHeaderFooterItem )item; CollectionViewGroup parentGroup = groupHeaderFooterItem.Group; if( this.GetGroupFromCollectionViewGroup( parentGroup ) != null ) { //since the goal is the find the parentGroup from the item passed (which is a GroupHeader or GroupFooter), then the Group //is what I am looking for. collectionViewGroup = parentGroup; return true; } //Item was a GroupHeaderFooterItem but was not part of the genreator! return false; } //----------------------------------------------- //4 - Final Check //----------------------------------------------- //if the item was not generated, then try to find the item as is within the generator's content GeneratorNodeHelper finalNodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); if( finalNodeHelper.AbsoluteFindItem( item ) ) { //item was not generated but was part of this generator if( finalNodeHelper.CurrentNode.Parent == null ) { //no parent for speficied item. return true; } collectionViewGroup = ( ( GroupGeneratorNode )finalNodeHelper.CurrentNode.Parent ).CollectionViewGroup; return true; } else { return false; } //Default function behavior, if nobody else returned, then the function did not work as intended... as each "block" is supposed to return a value... throw new DataGridInternalException(); }
private void OnGeneratorNodeGroupsCollectionChanged( object sender, NotifyCollectionChangedEventArgs e ) { if( this.Status == GeneratorStatus.GeneratingContainers ) { throw new InvalidOperationException( "Cannot perform this operation while the generator is busy generating items" ); } GroupGeneratorNode node = sender as GroupGeneratorNode; if( node != null ) { switch( e.Action ) { case NotifyCollectionChangedAction.Add: int addCount; GeneratorNode addNode = this.HandleParentGroupAddition( node, out addCount, e ); if( node.IsComputedExpanded ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( addNode, 0, 0 );//index not important, will reserve find it. nodeHelper.ReverseCalculateIndex(); this.IncrementCurrentGenerationCount(); GeneratorPosition genPos = this.GeneratorPositionFromIndex( nodeHelper.Index ); this.SendAddEvent( genPos, nodeHelper.Index, addCount ); } break; case NotifyCollectionChangedAction.Move: if( node.Child == null ) { throw new DataGridInternalException(); } else { Debug.Assert( e.OldStartingIndex != e.NewStartingIndex, "An attempt was made to move a group to the same location." ); #if LOG Log.Assert( this, e.OldStartingIndex != e.NewStartingIndex, "An attempt was made to move a group to the same location." ); #endif this.HandleSameLevelGroupMove( node.Child, e ); } break; case NotifyCollectionChangedAction.Remove: if( node.Child == null ) { throw new DataGridInternalException(); } else { int remCount; int generatedRemCount; int removeIndex; List<DependencyObject> removedContainers = new List<DependencyObject>(); GeneratorPosition remPos = this.HandleParentGroupRemove( node, out remCount, out generatedRemCount, out removeIndex, e, removedContainers ); if( node.IsComputedExpanded ) { this.IncrementCurrentGenerationCount(); this.SendRemoveEvent( remPos, removeIndex, remCount, generatedRemCount, removedContainers ); } } break; case NotifyCollectionChangedAction.Replace: //if( node.Child == null ) //{ // throw new DataGridInternalException(); //} //else //{ // this.HandleGroupReplace( node.Child, e ); //} //m_currentGeneratorContentGeneration++; throw new DataGridInternalException(); //break; case NotifyCollectionChangedAction.Reset: //m_currentGeneratorContentGeneration++; throw new DataGridInternalException(); default: throw new DataGridInternalException(); } } }
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 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 ); } }
public int GetGroupIndex( Group group ) { if( group == null ) throw new ArgumentNullException( "group" ); this.EnsureNodeTreeCreated(); GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); if( nodeHelper.FindGroup( group.CollectionViewGroup ) ) return nodeHelper.Index; //the item was not found in this generator's content... check all the detail generators foreach( KeyValuePair<object, List<DetailGeneratorNode>> itemToDetails in m_masterToDetails ) { foreach( DetailGeneratorNode detailNode in itemToDetails.Value ) { int groupIndex = detailNode.DetailGenerator.GetGroupIndex( group ); if( groupIndex > -1 ) return groupIndex + this.FindGlobalIndexForDetailNode( detailNode ); } } return -1; }
public Group GetGroupFromItem( object item ) { Group retval = null; GeneratorNode nodeForItem = null; this.EnsureNodeTreeCreated(); //item might be in the "generated" list... much quicker to find-out if it is! int itemGenPosIndex = this.FindFirstGeneratedIndexForLocalItem( item ); if( itemGenPosIndex != -1 ) { //item is generated... nodeForItem = m_genPosToNode[ itemGenPosIndex ]; } else { //only try to find the item is the generator has some content... if( m_startNode != null ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); if( nodeHelper.Contains( item ) ) //NOTE: this will only return items directly contained in this generator (not from details ) { //if the nodeHelper was able to locate the content, use the nodeHelper's CurrentNode as the node for the item. nodeForItem = nodeHelper.CurrentNode; } else { throw new InvalidOperationException( "An attempt was made to retrieve the group of an item that does not belong to the generator." ); } } } if( nodeForItem != null ) { GroupGeneratorNode parentGroup = nodeForItem.Parent as GroupGeneratorNode; if( parentGroup != null ) { retval = parentGroup.UIGroup; } } return retval; }
private GeneratorNode HandleParentGroupAddition( GeneratorNode parent, out int countAdded, NotifyCollectionChangedEventArgs e ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( parent, 0, 0 ); //do not care about index (for now). //start by moving to the first child... of the node (GroupHeaders node, most probably). if( !nodeHelper.MoveToChild( false ) ) //case 120137: false parameter is to prevent skipping over a collapsed node (item count 0 ) { //could not advance to the child item so there is no items to be removed... throw new DataGridInternalException(); } return this.HandleSameLevelGroupAddition( nodeHelper.CurrentNode, out countAdded, e ); }
private void HandleItemRemoveMoveReplace( ItemsGeneratorNode node, NotifyCollectionChangedEventArgs e ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( node, 0, 0 ); //index not important for now. nodeHelper.ReverseCalculateIndex(); node.AdjustItemCount( -e.OldItems.Count ); node.AdjustLeafCount( -e.OldItems.Count ); int nodeStartIndex = e.OldStartingIndex; int nodeEndIndex = nodeStartIndex + e.OldItems.Count - 1; int detailCountToRemove = CustomItemContainerGenerator.ComputeDetailsCount( node, nodeStartIndex, nodeEndIndex ); int detailCountBeforeRemovedItems = 0; if( nodeStartIndex > 0 ) { detailCountBeforeRemovedItems = CustomItemContainerGenerator.ComputeDetailsCount( node, 0, nodeStartIndex - 1 ); } int startIndex = nodeHelper.Index + e.OldStartingIndex + detailCountBeforeRemovedItems; int endIndex = startIndex + detailCountToRemove + e.OldItems.Count - 1; int removeCount = e.OldItems.Count + detailCountToRemove; int replaceCount = ( e.Action == NotifyCollectionChangedAction.Replace ) ? e.NewItems.Count : 0; // *** RemoveDetails must be done before GeneratorPositionFromIndex, since GeneratorPositionFromIndex will indirectly do a RemapFloatingDetails // *** that will cause the index to already be rectified and make a double rectification to occurs. //Remove the details from the ItemsGeneratorNode and re-index the other details appropriatly. this.RemoveDetails( node, nodeStartIndex, nodeEndIndex, replaceCount ); GeneratorPosition removeGenPos = this.GeneratorPositionFromIndex( startIndex ); //Try to remap the old item for detail remapping (will do nothing if item has no details ) foreach( object oldItem in e.OldItems ) { this.QueueDetailItemForRemapping( oldItem ); } //if the node is totally expanded if( node.IsComputedExpanded ) { List<DependencyObject> removedContainers = new List<DependencyObject>(); int genRemCount = this.RemoveGeneratedItems( startIndex, endIndex, removedContainers ); this.IncrementCurrentGenerationCount(); this.SendRemoveEvent( removeGenPos, startIndex, removeCount, genRemCount, removedContainers ); } //then, based on the action that was performed (move, replace or remove) switch( e.Action ) { case NotifyCollectionChangedAction.Move: this.OffsetDetails( node, e.NewStartingIndex, e.NewItems.Count ); this.HandleItemMoveRemoveReplaceHelper( node, e, nodeHelper.Index ); break; case NotifyCollectionChangedAction.Replace: this.HandleItemMoveRemoveReplaceHelper( node, e, nodeHelper.Index ); break; case NotifyCollectionChangedAction.Remove: // Do nothing! break; case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Reset: default: throw new DataGridInternalException(); } }
private void HandleSameLevelGroupMove( GeneratorNode node, NotifyCollectionChangedEventArgs e ) { GroupGeneratorNode parentGroup = node.Parent as GroupGeneratorNode; //Start a NodeHelper on the first child of the node where the move occured. GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( node, 0, 0 ); nodeHelper.ReverseCalculateIndex(); //determine index of the node. //Advance to the first "Group" node (skip the GroupHEaders) while( !( nodeHelper.CurrentNode is GroupGeneratorNode ) ) { if( !nodeHelper.MoveToNext() ) throw new DataGridInternalException(); } //then move up to the removal start point. if( !nodeHelper.MoveToNextBy( e.OldStartingIndex ) ) { throw new DataGridInternalException(); } //remember the current node as the start point of the move (will be used when "extracting the chain") GeneratorNode startNode = nodeHelper.CurrentNode; //also remember the index of the node, to calculate range of elements to remove (containers ) int startIndex = nodeHelper.Index; //then, cumulate the total number of items in the groups concerned int totalCountRemoved = 0; node = this.ProcessGroupRemoval( startNode, e.OldItems.Count, false, out totalCountRemoved ); //send a message to the panel to remove the visual elements concerned GeneratorPosition removeGenPos = this.GeneratorPositionFromIndex( startIndex ); List<DependencyObject> removedContainers = new List<DependencyObject>(); int genCountRemoved = this.RemoveGeneratedItems( startIndex, startIndex + totalCountRemoved - 1, removedContainers ); this.SendRemoveEvent( removeGenPos, startIndex, totalCountRemoved, genCountRemoved, removedContainers ); //reset the node parameter for the "re-addition" node = ( parentGroup != null ) ? parentGroup.Child : m_firstItem; if( node == null ) throw new DataGridInternalException(); //Once the chain was pulled out, re-insert it at the appropriate location. nodeHelper = new GeneratorNodeHelper( node, 0, 0 ); //do not care about the index for what I need //Advance to the first "Group" node (skip the GroupHEaders) while( !( nodeHelper.CurrentNode is GroupGeneratorNode ) ) { if( !nodeHelper.MoveToNext() ) throw new DataGridInternalException(); } bool insertBefore = nodeHelper.MoveToNextBy( e.NewStartingIndex ); if( insertBefore ) { if( nodeHelper.CurrentNode == m_firstItem ) { if( m_startNode == m_firstItem ) { m_startNode = startNode; } m_firstItem = startNode; } //reinsert the chain at the specified location. nodeHelper.InsertBefore( startNode ); } else { nodeHelper.InsertAfter( startNode ); } //and finally, call to increment the generation count for the generator content this.IncrementCurrentGenerationCount(); }
private void OnGeneratorNodeExpansionStateChanged( object sender, ExpansionStateChangedEventArgs e ) { //throw an error is the Generator is actually busy generating! if( this.Status == GeneratorStatus.GeneratingContainers ) { throw new InvalidOperationException( "Cannot perform this operation while the generator is busy generating items" ); } GeneratorNode node = sender as GeneratorNode; Debug.Assert( node != null, "node != null" ); #if LOG Log.Assert( this, node != null, "node != null" ); #endif if( node == null ) return; GroupGeneratorNode changedNode = node.Parent as GroupGeneratorNode; Debug.Assert( changedNode != null, "changedNode != null" ); //should never be null, as the "node" is supposed to be the child node of this one. #if LOG Log.Assert( this, changedNode != null, "changedNode != null" ); //should never be null, as the "node" is supposed to be the child node of this one. #endif //Determine if the changedNode is "below" a collapsed group (because if so, I don't need any sort of notification or removal ). GroupGeneratorNode parentGroupNode = changedNode.Parent as GroupGeneratorNode; if( ( parentGroupNode != null ) && ( !parentGroupNode.IsComputedExpanded ) ) return; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( node, 0, 0 ); nodeHelper.ReverseCalculateIndex(); //if the node was "Collapsed" if( !e.NewExpansionState ) { int removeCount = e.Count; int startIndex = nodeHelper.Index + e.IndexOffset; GeneratorPosition removeGenPos = this.GeneratorPositionFromIndex( startIndex ); List<DependencyObject> removedContainers = new List<DependencyObject>(); //remove the Generated items between the appropriate indexes int removeUICount = this.RemoveGeneratedItems( startIndex, startIndex + removeCount - 1, removedContainers ); if( removeCount > 0 ) { //send the event so the panel can remove the group elements this.SendRemoveEvent( removeGenPos, startIndex, removeCount, removeUICount, removedContainers ); } } //if the node was "Expanded" else { int addCount = e.Count; int startIndex = nodeHelper.Index + e.IndexOffset; GeneratorPosition addGenPos = this.GeneratorPositionFromIndex( startIndex ); if( addCount > 0 ) { //send the event so the panel can add the group elements this.SendAddEvent( addGenPos, startIndex, addCount ); } } }
private void OnGroupsChanged( object sender, NotifyCollectionChangedEventArgs e ) { // Avoid re-entrance when processing a global reset if( m_isProcessingGlobalResetOrRemovingAllGeneratedItemsDisposableCount > 0 ) return; if( this.Status == GeneratorStatus.GeneratingContainers ) { throw new InvalidOperationException( "Cannot perform this operation while the generator is busy generating items" ); } //this fonction is only used to process the content of the DataGridControl.Items.Groups collection... // for the CollectionChanged event of branch groups ( IsBottomLevel = false ), refer to the // OnBranchGroupsChanged fonction switch( e.Action ) { case NotifyCollectionChangedAction.Add: int addCount = e.NewItems.Count; GeneratorPosition genPos = new GeneratorPosition( -1, 1 ); //this would map to the first item in the list if not generated. int addIndex = -1; //if the first item is empty, do not do anything, the structure will be generated when the generator is started! if( m_firstItem != null ) { //The only moment where the m_firstItem is null is typically when a reset occured... //other moments is when there are 0 items (in which case, the. GeneratorNode addNode = this.HandleSameLevelGroupAddition( m_firstItem, out addCount, e ); this.IncrementCurrentGenerationCount(); GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( addNode, 0, 0 );//index not important, will reserve find it. nodeHelper.ReverseCalculateIndex(); addIndex = nodeHelper.Index; genPos = this.GeneratorPositionFromIndex( addIndex ); } this.SendAddEvent( genPos, addIndex, addCount ); break; case NotifyCollectionChangedAction.Move: if( m_firstItem != null ) { if( !( m_firstItem is GroupGeneratorNode ) ) { throw new DataGridInternalException(); } Debug.Assert( e.OldStartingIndex != e.NewStartingIndex, "An attempt was made to move a group to the same location." ); #if LOG Log.Assert( this, e.OldStartingIndex != e.NewStartingIndex, "An attempt was made to move a group to the same location." ); #endif this.HandleSameLevelGroupMove( m_firstItem, e ); } break; case NotifyCollectionChangedAction.Remove: if( m_firstItem != null ) { if( !( m_firstItem is GroupGeneratorNode ) ) { throw new DataGridInternalException(); } int remCount; int generatedRemCount; int removeIndex; List<DependencyObject> removedContainers = new List<DependencyObject>(); GeneratorPosition remPos = this.HandleSameLevelGroupRemove( m_firstItem, out remCount, out generatedRemCount, out removeIndex, e, removedContainers ); //there is no need to check if the parent node is expanded or not... since the first level of group cannot be collapsed. this.IncrementCurrentGenerationCount(); this.SendRemoveEvent( remPos, removeIndex, remCount, generatedRemCount, removedContainers ); } break; case NotifyCollectionChangedAction.Replace: throw new NotSupportedException( "Replace not supported at the moment on groups!!!" ); //break; case NotifyCollectionChangedAction.Reset: //I'm forced to handle it specifically since the Panel will AUTOMATICALLY clear its children this.HandleGlobalItemsReset(); break; default: throw new DataGridInternalException(); } }
void ICustomItemContainerGenerator.SetCurrentIndex( int newCurrentIndex ) { // This method enables the possibility for the "ItemsHost" panels to set the current item of the // DataGridControl. This is required for scenarios such as the CardflowItemsHost. this.EnsureNodeTreeCreated(); GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); //First, locate the item within the Generator. object newCurrentItem = nodeHelper.FindIndex( newCurrentIndex ); if( newCurrentItem == null ) throw new InvalidOperationException( "An attempt was made to access an item at an index that does not correspond to an item." ); //Then, if the item is within an ItemsNode, check if it belongs to a detail ItemsGeneratorNode itemsNode = nodeHelper.CurrentNode as ItemsGeneratorNode; if( itemsNode != null ) { int masterIndex; int detailIndex; int detailNodeIndex; DetailGeneratorNode detailNode = itemsNode.GetDetailNodeForIndex( newCurrentIndex - nodeHelper.Index, out masterIndex, out detailIndex, out detailNodeIndex ); //If it belongs to a detail if( detailNode != null ) { //call recursively the SetCurrentIndex method on the detail generator to ensure that if the item //belongs to a sub-generator of the detail generator, then the appropriate DataGridContext will get invoked. ICustomItemContainerGenerator detailGenerator = detailNode.DetailGenerator; detailGenerator.SetCurrentIndex( detailIndex ); return; } } //If the item is not within an ItemsNode or not within a detail //then set it current within its context. m_dataGridContext.SetCurrentItemCore( newCurrentItem, false, false ); }
internal int GetStickyFooterCountForIndex( int index, bool areFootersSticky, bool areGroupFootersSticky ) { int stickyFooterCount = 0; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); if( !nodeHelper.FindNodeForIndex( index ) ) { // Unable to find the node, no sticky header for this node return 0; } GeneratorNode indexNode = nodeHelper.CurrentNode; ItemsGeneratorNode itemsGeneratorNode = indexNode as ItemsGeneratorNode; // We found an ItemsGeneratorNode if( itemsGeneratorNode != null ) { GeneratorNode innerNode = itemsGeneratorNode.GetDetailNodeForIndex( index - nodeHelper.Index ); DetailGeneratorNode detailNode = innerNode as DetailGeneratorNode; if( detailNode != null ) { int detailFirstRealizedIndex = this.FindGlobalIndexForDetailNode( detailNode ); int correctedIndex = index - detailFirstRealizedIndex; Debug.Assert( correctedIndex >= 0, "correctedIndex >= 0 .. 2" ); #if LOG Log.Assert( this, correctedIndex >= 0, "correctedIndex >= 0 .. 2" ); #endif stickyFooterCount += detailNode.DetailGenerator.GetStickyFooterCountForIndex( correctedIndex, areFootersSticky, areGroupFootersSticky ); } } // 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; // There is no footers to generate if the item count of the node is 0. if( footersNode.ItemCount > 0 ) { if( ( ( footersNode.Parent == null ) && ( areFootersSticky ) ) || ( ( footersNode.Parent is GroupGeneratorNode ) && ( areGroupFootersSticky ) ) ) { // We are generating sticky footers for a container that is already // part of the footers, we need to handle it differently. stickyFooterCount += this.GetStickyFooterCountForNode( footersNode, nodeHelper.Index, index, ( footersNode != indexNode ) ); } } // 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( ( bottomFootersNode != null ) && ( bottomFootersNode != footersNode ) && ( bottomFootersNode.ItemCount > 0 ) && ( areFootersSticky ) ) { stickyFooterCount += this.GetStickyFooterCountForNode( bottomFootersNode, nodeHelper.Index ); } return stickyFooterCount; }
private GeneratorNode HandleSameLevelGroupAddition( GeneratorNode firstChild, out int countAdded, NotifyCollectionChangedEventArgs e ) { Debug.Assert( ( ( firstChild.Parent == null ) || ( firstChild.Parent is GroupGeneratorNode ) ), "parent of the node should be a GroupGeneratorNode" ); #if LOG Log.Assert( this, ( ( firstChild.Parent == null ) || ( firstChild.Parent is GroupGeneratorNode ) ), "parent of the node should be a GroupGeneratorNode" ); #endif GeneratorNode newNodeChain = this.CreateGroupListFromCollection( e.NewItems, firstChild.Parent ); countAdded = 0; if( newNodeChain != null ) { int chainLength; GeneratorNodeHelper.EvaluateChain( newNodeChain, out countAdded, out chainLength ); GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( firstChild, 0, 0 ); //do not care about index. //Advance to the first "Group" node (skip the GroupHEaders) while( !( nodeHelper.CurrentNode is GroupGeneratorNode ) ) { if( !nodeHelper.MoveToNext() ) { //if there are no items and no groups in the Group Node, then we will never find a GroupGeneratorNode... //However, the structure of the group/headers/footers/group headers/groups footers makes it so //that inserting before the last item (footer/group footer) will place the group at the appropriate location. break; } } bool insertAfter = false; //If there is 0 group in the parent group, then this loop will exit without executing the control block once... for( int i = 0; i < e.NewStartingIndex; i++ ) { if( !nodeHelper.MoveToNext() ) { insertAfter = true; } } //if we are inserting past the end of the linked list level. if( insertAfter ) { nodeHelper.InsertAfter( newNodeChain ); } else { //we are inserting in the middle of the list nodeHelper.InsertBefore( newNodeChain ); } //If the insertion point is the beginning, check that the node pointers are updated properly if( ( e.NewStartingIndex == 0 ) && ( ( firstChild.Parent == null ) ) ) { if( m_startNode == m_firstItem ) { m_startNode = newNodeChain; } m_firstItem = newNodeChain; } } return newNodeChain; }
private void UpdateHeaders( IList headers ) { //if there was no header prior this udpate. if( m_firstHeader == null ) { //create the node(s) that would contain the headers m_firstHeader = this.CreateHeaders( headers ); int count; int chainLength; GeneratorNodeHelper.EvaluateChain( m_firstHeader, out count, out chainLength ); //if there was items present in the linked list. if( m_startNode != null ) { //headers are automatically inserted at the beginning GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); nodeHelper.InsertBefore( m_firstHeader ); } else { #if LOG Log.WriteLine( this, "UpdateHeaders - new tree" ); #endif } //set the start node as the first header node. m_startNode = m_firstHeader; } //If the m_firstHeader is not NULL, then there is nothing to do, since the Header node contains an observable collection //which we monitor otherwise. }
private void HandleItemAddition( GeneratorNode node, NotifyCollectionChangedEventArgs e ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( node, 0, 0 ); //index not important for now. node.AdjustItemCount( e.NewItems.Count ); ItemsGeneratorNode itemsNode = node as ItemsGeneratorNode; if( itemsNode != null ) { itemsNode.AdjustLeafCount( e.NewItems.Count ); this.OffsetDetails( itemsNode, e.NewStartingIndex, e.NewItems.Count ); } //if the node is totally expanded if( node.IsComputedExpanded ) { nodeHelper.ReverseCalculateIndex(); //invalidate the indexes this.IncrementCurrentGenerationCount(); int startIndex = nodeHelper.Index + e.NewStartingIndex; GeneratorPosition addGenPos = this.GeneratorPositionFromIndex( startIndex ); //and send notification message this.SendAddEvent( addGenPos, startIndex, e.NewItems.Count ); } }
private void UpdateFooters( IList footers ) { //if there was no header prior this udpate. if( m_firstFooter == null ) { //create the node(s) that would contain the footers m_firstFooter = this.CreateFooters( footers ); //if there are no footers, then the m_firstFooter will remain null if( m_firstFooter != null ) { int count; int chainLength; GeneratorNodeHelper.EvaluateChain( m_firstFooter, out count, out chainLength ); //if there was items present in the linked list. if( m_startNode != null ) { //since we called ClearFooters earlier, I can just go at the end of the list of items GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); nodeHelper.MoveToEnd(); nodeHelper.InsertAfter( m_firstFooter ); } else { m_startNode = m_firstFooter; #if LOG Log.WriteLine( this, "UpdateFooters - new tree" ); #endif } } } //If the m_firstHeader is not NULL, then there is nothing to do, since the Header node contains an observable collection //which we monitor otherwise. }
public object ItemFromIndex( int index ) { object retval = null; this.EnsureNodeTreeCreated(); GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); retval = nodeHelper.FindIndex( index ); return retval; }
private GeneratorNode SetupInitialItemsNodes( out int addCount ) { //TODO ( case 117275 ): Check the amount of time this is executed for a single grid being populated! GeneratorNode newItemNode = null; addCount = 0; this.RefreshGroupsCollection(); //this function should only be called when the m_firstItem is null. if( m_groupsCollection != null ) { newItemNode = this.CreateGroupListFromCollection( m_groupsCollection, null ); } else { newItemNode = this.CreateStandaloneItemsNode(); } //if the node is non-null, then that's because there was items in the collection if( newItemNode != null ) { m_firstItem = newItemNode; int chainLength; GeneratorNodeHelper.EvaluateChain( m_firstItem, out addCount, out chainLength ); //find the appropriate point to inject the Items nodes... if( m_startNode != null ) { //if there is a footer node, then the insertion point is just before the footer node (if there is anything before!) if( m_firstFooter != null ) { GeneratorNode originalPrevious = m_firstFooter.Previous; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_firstFooter, 0, 0 ); //do not care about index! nodeHelper.InsertBefore( m_firstItem ); if( originalPrevious == null ) //that means that the first footer is the first item { m_startNode = newItemNode; } } else if( m_firstHeader != null ) //if there is no footer but some headers, add it at the end. { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_firstHeader, 0, 0 ); //do not care about index! nodeHelper.MoveToEnd(); nodeHelper.InsertAfter( m_firstItem ); } else { throw new DataGridInternalException(); //this case should not be possible: no header, no footers but there is a startNode } } else { m_startNode = m_firstItem; #if LOG Log.WriteLine( this, "SetupInitialItemsNodes - new tree" ); #endif } } return newItemNode; }
private void HandleHeaderFooterRemove( HeadersFootersGeneratorNode node, NotifyCollectionChangedEventArgs e ) { bool itemsRemoved = false; node.AdjustItemCount( -e.OldItems.Count ); //if the node is totally expanded if( node.IsComputedExpanded ) { GroupGeneratorNode parentGroup = node.Parent as GroupGeneratorNode; int removeGenPosIndex; int removeIndex; foreach( object item in e.OldItems ) { object realItem = ( parentGroup != null ) ? new GroupHeaderFooterItem( parentGroup.CollectionViewGroup, item ) : item; removeGenPosIndex = m_genPosToItem.IndexOf( realItem ); GeneratorPosition removeGenPos; // If the value is -1, it means the Header/Footer was not realized when the remove occured. if( removeGenPosIndex != -1 ) { removeIndex = m_genPosToIndex[ removeGenPosIndex ]; removeGenPos = new GeneratorPosition( removeGenPosIndex, 0 ); } else { //Since there is no way to get the item's index from the list of generated items, then //compute it based on the node's index and the event args parameters. GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( node, 0, 0 ); nodeHelper.ReverseCalculateIndex(); removeIndex = nodeHelper.Index + e.OldStartingIndex; removeGenPos = this.GeneratorPositionFromIndex( removeIndex ); } List<DependencyObject> removedContainers = new List<DependencyObject>(); this.RemoveGeneratedItems( node, realItem, removedContainers ); this.SendRemoveEvent( removeGenPos, removeIndex, 1, removedContainers.Count, removedContainers ); itemsRemoved = true; } this.IncrementCurrentGenerationCount( itemsRemoved ); } }
private GeneratorNode CreateGroupListFromCollection( IList collection, GeneratorNode parentNode ) { GeneratorNode rootNode = null; GeneratorNode previousNode = null; GroupGeneratorNode actualNode = null; GeneratorNode childNode = null; int level = ( parentNode == null ) ? 0 : parentNode.Level + 1; GroupConfiguration groupConfig; bool initiallyExpanded; ObservableCollection<GroupDescription> groupDescriptions = DataGridContext.GetGroupDescriptionsHelper( m_collectionView ); GroupConfigurationSelector groupConfigurationSelector = m_dataGridContext.GroupConfigurationSelector; foreach( CollectionViewGroup group in collection ) { groupConfig = GroupConfiguration.GetGroupConfiguration( m_dataGridContext, groupDescriptions, groupConfigurationSelector, level, group ); Debug.Assert( groupConfig != null, "groupConfig != null" ); #if LOG Log.Assert( this, groupConfig != null, "groupConfig != null" ); #endif if( groupConfig.UseDefaultHeadersFooters ) groupConfig.AddDefaultHeadersFooters(); initiallyExpanded = groupConfig.InitiallyExpanded; actualNode = ( GroupGeneratorNode )this.NodeFactory.CreateGroupGeneratorNode( group, parentNode, previousNode, null, groupConfig ); m_groupNodeMappingCache.Add( group, actualNode ); if( rootNode == null ) { rootNode = actualNode; } previousNode = actualNode; actualNode.UIGroup = new Group( actualNode, group, m_dataGridContext.GroupLevelDescriptions, m_dataGridContext ); //Independently if the Group is the bottom level or not, we need to setup GroupHeaders childNode = this.SetupGroupHeaders( groupConfig, actualNode ); actualNode.Child = childNode; GeneratorNodeHelper childNodeHelper = new GeneratorNodeHelper( childNode, 0, 0 ); //do not care about index. childNodeHelper.MoveToEnd(); //extensibility, just in case SetupGroupHeaders() ever return a node list. IList<object> subItems = group.GetItems(); //if the node newly created is not the bottom level if( !group.IsBottomLevel ) { if( ( subItems != null ) && ( subItems.Count > 0 ) ) { GeneratorNode subGroupsNode = this.CreateGroupListFromCollection( subItems as IList, actualNode ); if( subGroupsNode != null ) { childNodeHelper.InsertAfter( subGroupsNode ); } } } else { //this is the bottom level, create an Items node GeneratorNode itemsNode = this.NodeFactory.CreateItemsGeneratorNode( subItems as IList, actualNode, null, null, this ); if( itemsNode != null ) { childNodeHelper.InsertAfter( itemsNode ); } } childNodeHelper.InsertAfter( this.SetupGroupFooters( groupConfig, actualNode ) ); } return rootNode; }
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 }
private GeneratorPosition HandleParentGroupRemove( GeneratorNode parent, out int countRemoved, out int genCountRemoved, out int removeIndex, NotifyCollectionChangedEventArgs e, IList<DependencyObject> removedContainers ) { GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( parent, 0, 0 ); //do not care about index (for now). // start by moving to the first child... of the node (GroupHeaders node, most probably). // false parameter is to prevent skipping over a collapsed node (item count 0 ) if( !nodeHelper.MoveToChild( false ) ) { //could not advance to the child item so there is no items to be removed... throw new DataGridInternalException(); } return this.HandleSameLevelGroupRemove( nodeHelper.CurrentNode, out countRemoved, out genCountRemoved, out removeIndex, e, removedContainers ); }
private void StartGenerator( GeneratorPosition startPos, GeneratorDirection direction ) { if( this.Status == GeneratorStatus.GeneratingContainers ) { throw new InvalidOperationException( "Cannot perform this operation while the generator is busy generating items" ); } //set the GeneratorStatus to "Generating" m_generatorStatus = GeneratorStatus.GeneratingContainers; //Initialize the Direction m_generatorDirection = direction; //retrieve the Index for the GeneratorPosition retrieved m_generatorCurrentGlobalIndex = this.IndexFromGeneratorPosition( startPos ); // case 117460: throw an exception if the GeneratorPosition is bad, but not if the itemcount is 0 //(and generator position maps to index 0 ). int itemCount = this.ItemCount; if( ( m_generatorCurrentGlobalIndex < 0 ) || ( ( itemCount > 0 ) && ( m_generatorCurrentGlobalIndex >= itemCount ) ) ) throw new ArgumentOutOfRangeException( "startPos", "The specified start position is outside the range of the Generator content." ); //case 117460: if the item count is 0, return without doing any check, GenerateNext will never process any content in that case. //Since the GeneratorNodeHelper is never created. if( itemCount == 0 ) return; //and create a node helper that will assist us during the Generation process... m_generatorNodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); // start index is always 0 //position the GeneratorNodeHelper to the appropriate node if( !m_generatorNodeHelper.FindNodeForIndex( m_generatorCurrentGlobalIndex ) ) //find index?!?! { //there was a problem moving the Node helper... throw new DataGridInternalException(); } //Calculate the offset m_generatorCurrentOffset = m_generatorCurrentGlobalIndex - m_generatorNodeHelper.Index; ItemsGeneratorNode itemsNode = m_generatorNodeHelper.CurrentNode as ItemsGeneratorNode; if( itemsNode != null ) { m_generatorCurrentDetail = itemsNode.GetDetailNodeForIndex( m_generatorCurrentOffset, out m_generatorCurrentOffset, out m_generatorCurrentDetailIndex, out m_generatorCurrentDetailNodeIndex ); } if( m_generatorCurrentDetail != null ) { m_generatorCurrentDetailDisposable = ( ( IItemContainerGenerator )m_generatorCurrentDetail.DetailGenerator ).StartAt( m_generatorCurrentDetail.DetailGenerator.GeneratorPositionFromIndex( m_generatorCurrentDetailIndex ), direction, true ); } }
private GeneratorPosition HandleSameLevelGroupRemove( GeneratorNode firstChild, out int countRemoved, out int genCountRemoved, out int removeIndex, NotifyCollectionChangedEventArgs e, IList<DependencyObject> removedContainers ) { GeneratorPosition retval; countRemoved = 0; genCountRemoved = 0; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( firstChild, 0, 0 ); nodeHelper.ReverseCalculateIndex(); //Advance to the first "Group" node (skip the GroupHEaders) while( !( nodeHelper.CurrentNode is GroupGeneratorNode ) ) { if( !nodeHelper.MoveToNext() ) throw new DataGridInternalException(); } //then move up to the removal start point. if( !nodeHelper.MoveToNextBy( e.OldStartingIndex ) ) { throw new DataGridInternalException(); } GroupGeneratorNode startNode = nodeHelper.CurrentNode as GroupGeneratorNode; removeIndex = -1; //Only fetch the index if the group itself is not "collapsed" or under a collapsed group already if( ( startNode.IsExpanded == startNode.IsComputedExpanded ) && ( startNode.ItemCount > 0 ) ) { removeIndex = nodeHelper.Index; retval = this.GeneratorPositionFromIndex( removeIndex ); } else { retval = new GeneratorPosition( -1, 1 ); } //retrieve the generator position for the first item to remove. this.ProcessGroupRemoval( startNode, e.OldItems.Count, true, out countRemoved ); //Clean the chain "isolated" previously this.NodeFactory.CleanGeneratorNodeTree( startNode ); if( removeIndex != -1 ) { //remove the appropriate genCountRemoved = this.RemoveGeneratedItems( removeIndex, removeIndex + countRemoved - 1, removedContainers ); } return retval; }
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 GroupGeneratorNode ProcessGroupRemoval( GeneratorNode startNode, int removeCount, bool updateGroupNodeMappingCache, out int countRemoved ) { Debug.Assert( removeCount != 0, "remove count cannot be 0" ); #if LOG Log.Assert( this, removeCount != 0, "remove count cannot be 0" ); #endif GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( startNode, 0, 0 );//index not important. GroupGeneratorNode parentGroup = startNode.Parent as GroupGeneratorNode; int i = 0; countRemoved = 0; do { GroupGeneratorNode group = nodeHelper.CurrentNode as GroupGeneratorNode; if( updateGroupNodeMappingCache ) { m_groupNodeMappingCache.Remove( group.CollectionViewGroup ); } Debug.Assert( group != null, "node to be removed must be a GroupGeneratorNode" ); #if LOG Log.Assert( this, group != null, "node to be removed must be a GroupGeneratorNode" ); #endif //add the total number of child to the count of items removed. countRemoved += group.ItemCount; i++; if( i < removeCount ) { if( !nodeHelper.MoveToNext() ) { //could not advance to the last item to be removed... throw new DataGridInternalException(); } } } while( i < removeCount ); //disconnect the node chain to be removed from the linked list. GeneratorNode previous = startNode.Previous; GeneratorNode next = nodeHelper.CurrentNode.Next; if( next != null ) { next.Previous = previous; } if( previous != null ) { previous.Next = next; } //if the first node removed was the first child of its parent if( ( parentGroup != null ) && ( parentGroup.Child == startNode ) ) { //set the next in line after the chain to be removed as the first child parentGroup.Child = next; } //break the link between the chain and its same-level siblings. nodeHelper.CurrentNode.Next = null; startNode.Previous = null; //Here, I need a special handling case... If I remove the first group node, I need to set a new firstItem if( startNode == m_firstItem ) { if( next != m_firstFooter ) { m_firstItem = next; } else { m_firstItem = null; } if( m_startNode == startNode ) { m_startNode = next; } } Debug.Assert( nodeHelper.CurrentNode is GroupGeneratorNode, "last node is not a GroupGeneratorNode" ); #if LOG Log.Assert( this, nodeHelper.CurrentNode is GroupGeneratorNode, "last node is not a GroupGeneratorNode" ); #endif return ( GroupGeneratorNode )nodeHelper.CurrentNode; }
void IDataGridContextVisitable.AcceptVisitor( int minIndex, int maxIndex, IDataGridContextVisitor visitor, DataGridContextVisitorType visitorType, bool visitDetails, out bool visitWasStopped ) { visitWasStopped = false; if( m_startNode == null ) return; GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 ); nodeHelper.ProcessVisit( m_dataGridContext, minIndex, maxIndex, visitor, visitorType, visitDetails, out visitWasStopped ); }
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; }