public GeneratorNode CreateItemsGeneratorNode(
            IList collection,
            GeneratorNode parent,
            GeneratorNode previous,
            GeneratorNode next)
        {
            Debug.Assert(collection != null, "collection cannot be null for CreateItemsGeneratorNode()");

            INotifyCollectionChanged notifyCollection = collection as INotifyCollectionChanged;

            Debug.Assert(notifyCollection != null, "collection must be a INotifyCollectionChanged for CreateItemsGeneratorNode()");

            ItemCollection itemCollection = collection as ItemCollection;

            if (itemCollection != null)
            {
                DataGridCollectionView dgcv = itemCollection.SourceCollection as DataGridCollectionView;
                if (dgcv != null)
                {
                    collection = dgcv;
                }
            }

            ItemsGeneratorNode node = new ItemsGeneratorNode(collection, parent);

            this.SetupCollectionGeneratorNode(node, parent, previous, next);

            node.AdjustLeafCount(node.Items.Count);

            return(node);
        }
Example #2
0
        public GeneratorNode CreateItemsGeneratorNode(
            IList collection,
            GeneratorNode parent,
            GeneratorNode previous,
            GeneratorNode next,
            CustomItemContainerGenerator generator)
        {
            Debug.Assert(collection != null, "collection cannot be null for CreateItemsGeneratorNode()");
            Debug.Assert(generator != null);

            INotifyCollectionChanged notifyCollection = collection as INotifyCollectionChanged;

            Debug.Assert(notifyCollection != null, "collection must be a INotifyCollectionChanged for CreateItemsGeneratorNode()");

            //case 113904: If the item source for the ItemsGeneratorNode is an ItemCollection, then
            //check if the underlying SourceCollection is a DataGridCollectionView.
            //This particular exception case is there to handle messaging quirks in the case
            //of Master Detail edition. Refer to case for more details.
            ItemCollection itemCollection = collection as ItemCollection;

            if (itemCollection != null)
            {
                DataGridCollectionView dgcv = itemCollection.SourceCollection as DataGridCollectionView;
                if (dgcv != null)
                {
                    collection = dgcv;
                }
            }

            ItemsGeneratorNode node = new ItemsGeneratorNode(collection, parent);

            this.SetupCollectionGeneratorNode(node, parent, previous, next);


            node.AdjustLeafCount(node.Items.Count);

            return(node);
        }
        public void CleanGeneratorNode(GeneratorNode node)
        {
            HeadersFootersGeneratorNode headersFootersNode = node as HeadersFootersGeneratorNode;

            if (headersFootersNode != null)
            {
                this.CleanHeadersFootersNotification(headersFootersNode);
            }
            else
            {
                ItemsGeneratorNode itemsNode = node as ItemsGeneratorNode;
                if (itemsNode != null)
                {
                    this.UnregisterNodeCollectionChanged(( INotifyCollectionChanged )itemsNode.Items);
                    itemsNode.CollectionChanged -= m_itemsChangedHandler;
                }
                else
                {
                    GroupGeneratorNode groupNode = node as GroupGeneratorNode;
                    if (groupNode != null)
                    {
                        IList <object> subItems = groupNode.CollectionViewGroup.GetItems();

                        this.UnregisterNodeCollectionChanged(( INotifyCollectionChanged )subItems);

                        groupNode.CollectionChanged  -= m_groupsChangedHandler;
                        groupNode.IsExpandedChanging -= m_isExpandedChangingHandler;
                        groupNode.IsExpandedChanged  -= m_isExpandedChangedHandler;
                    }
                }
            }

            node.ExpansionStateChanged -= m_expansionStateChangedHandler;

            node.CleanGeneratorNode();
        }
    private void HandleItemMoveRemoveReplaceHelper( ItemsGeneratorNode node, NotifyCollectionChangedEventArgs e, int nodeIndex )
    {
      node.AdjustItemCount( e.NewItems.Count );
      node.AdjustLeafCount( e.NewItems.Count );

      this.IncrementCurrentGenerationCount();

      //this is used to notify any Master Generator that it must update its content's status (UpdateGenPosToIndexList)
      this.SendAddEvent( new GeneratorPosition( -1, 1 ), nodeIndex + e.NewStartingIndex, e.NewItems.Count );
    }
    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 OffsetDetails( ItemsGeneratorNode node, int startIndex, int addOffset )
    {
      if( ( node.Details == null ) || ( node.Details.Count == 0 ) )
        return;

      int detailsCount = node.Details.Count;
      //first, create an array that will contain the keys for all the expanded details from the ItemsGeneratorNode
      int[] keys = new int[ detailsCount ];
      node.Details.Keys.CopyTo( keys, 0 );

      //sort the array, this will prevent any operation that will duplicate keys in the dictionary.
      Array.Sort<int>( keys );

      //loop from the end of the sorted array to the beginning. to ensuyre
      for( int i = detailsCount - 1; i >= 0; i-- )
      {
        int key = keys[ i ];

        //only process the key if it is in the processed range
        if( key >= startIndex )
        {
          List<DetailGeneratorNode> details;
          if( node.Details.TryGetValue( key, out details ) )
          {
#if LOG
            Log.WriteLine( this, "details.Offset for Add - IN" + node.GetHashCode().ToString() + "- Di" + key.ToString() + " - new Di" + ( key + addOffset ).ToString() );
#endif
            node.Details.Remove( key );
            node.Details.Add( key + addOffset, details );
          }
          else
          {
            //Key not found in the dictionary, something wrong is going on.
            throw new DataGridInternalException();
          }
        }
      }
    }
    private void SetupLastDetailForNode( ItemsGeneratorNode node )
    {
      List<DetailGeneratorNode> detailsForNode;

      if( ( node != null ) && ( node.Details != null )
        && ( node.Details.TryGetValue( m_generatorCurrentOffset, out detailsForNode ) ) )
      {
        for( int i = detailsForNode.Count - 1; i >= 0; i-- )
        {
          DetailGeneratorNode detailNode = detailsForNode[ i ];

          if( detailNode.ItemCount > 0 )
          {
            //There are details for the master item
            m_generatorCurrentDetailNodeIndex = i;
            m_generatorCurrentDetail = detailNode;
            m_generatorCurrentDetailIndex = detailNode.ItemCount - 1;
            break;
          }
        }
      }
    }
    private void UpdateDataVirtualizationLockForItemsNode( ItemsGeneratorNode itemsNode, object dataItem, bool applyLock )
    {
      IList items = itemsNode.Items;

      ItemCollection itemCollection = items as ItemCollection;

      if( itemCollection != null )
      {
        DataGridVirtualizingCollectionViewBase dataGridVirtualizingCollectionViewBase =
          itemCollection.SourceCollection as DataGridVirtualizingCollectionViewBase;

        if( dataGridVirtualizingCollectionViewBase != null )
        {
          int index = itemsNode.Items.IndexOf( dataItem );
          if( applyLock )
          {
            dataGridVirtualizingCollectionViewBase.RootGroup.LockGlobalIndex( index );
          }
          else
          {
            dataGridVirtualizingCollectionViewBase.RootGroup.UnlockGlobalIndex( index );
          }
        }
      }
      else
      {
        VirtualList virtualItemList = items as VirtualList;

        if( virtualItemList != null )
        {
          int index = itemsNode.Items.IndexOf( dataItem );

          if( applyLock )
          {
            virtualItemList.LockPageForLocalIndex( index );
          }
          else
          {
            virtualItemList.UnlockPageForLocalIndex( index );
          }
        }
      }

    }
    private static void ProcessItemsNodeVisit(
      ItemsGeneratorNode itemsNode,
      DataGridContext sourceContext,
      int minIndex,
      int maxIndex,
      IDataGridContextVisitor visitor,
      DataGridContextVisitorType visitorType,
      bool visitDetails,
      int sourceDataItemIndex,
      ref bool stopVisit )
    {
      int runningIndex = minIndex;
      sourceDataItemIndex += minIndex;

      int masterIndex;
      int detailStartIndex;
      int detailNodeIndex;

      while( runningIndex <= maxIndex )
      {
        DetailGeneratorNode detailNode = itemsNode.GetDetailNodeForIndex( runningIndex, out masterIndex, out detailStartIndex, out detailNodeIndex );

        if( detailNode != null )
        {
          int detailEndIndex = Math.Min( detailNode.ItemCount - 1, detailStartIndex + ( maxIndex - runningIndex ) );
          sourceDataItemIndex -= detailStartIndex;

          if( visitDetails )
          {
            bool visitWasStopped;

            ( ( IDataGridContextVisitable )detailNode.DetailGenerator ).AcceptVisitor(
              detailStartIndex, detailEndIndex, visitor, visitorType, visitDetails, out visitWasStopped );

            stopVisit = stopVisit || visitWasStopped;

            if( stopVisit )
              break;

            runningIndex += detailNode.ItemCount - detailStartIndex - 1;
          }
        }
        else
        {
          if( ( visitorType & DataGridContextVisitorType.Items ) == DataGridContextVisitorType.Items )
          {
            object dataItem = itemsNode.GetAt( runningIndex );
            visitor.Visit( sourceContext, sourceDataItemIndex, dataItem, ref stopVisit );

            if( stopVisit )
              break;
          }

          sourceDataItemIndex++;
        }

        runningIndex++;
      }
    }
Example #10
0
        private static void ProcessItemsNodeVisit(
            ItemsGeneratorNode itemsNode,
            DataGridContext sourceContext,
            int minIndex,
            int maxIndex,
            IDataGridContextVisitor visitor,
            DataGridContextVisitorType visitorType,
            bool visitDetails,
            int sourceDataItemIndex,
            ref bool stopVisit)
        {
            var runningIndex = minIndex;

            sourceDataItemIndex += minIndex;

            int masterIndex;
            int detailStartIndex;
            int detailNodeIndex;

            while (runningIndex <= maxIndex)
            {
                var detailNode = itemsNode.GetDetailNodeForIndex(runningIndex, out masterIndex, out detailStartIndex, out detailNodeIndex);

                if (detailNode != null)
                {
                    var detailEndIndex = Math.Min(detailNode.ItemCount - 1, detailStartIndex + (maxIndex - runningIndex));
                    sourceDataItemIndex -= detailStartIndex;

                    if (visitDetails)
                    {
                        bool visitWasStopped;

                        (( IDataGridContextVisitable )detailNode.DetailGenerator).AcceptVisitor(detailStartIndex, detailEndIndex, visitor, visitorType, visitDetails, out visitWasStopped);

                        stopVisit = stopVisit || visitWasStopped;

                        if (stopVisit)
                        {
                            break;
                        }

                        runningIndex += detailNode.ItemCount - detailStartIndex - 1;
                    }
                }
                else
                {
                    if ((visitorType & DataGridContextVisitorType.Items) == DataGridContextVisitorType.Items)
                    {
                        object dataItem = itemsNode.GetAt(runningIndex);
                        visitor.Visit(sourceContext, sourceDataItemIndex, dataItem, ref stopVisit);

                        if (stopVisit)
                        {
                            break;
                        }
                    }

                    sourceDataItemIndex++;
                }

                runningIndex++;
            }
        }
Example #11
0
        private static void ProcessItemsNodeBlockVisit(
            ItemsGeneratorNode itemsNode,
            DataGridContext sourceContext,
            int minIndex,
            int maxIndex,
            IDataGridContextVisitor visitor,
            DataGridContextVisitorType visitorType,
            bool visitDetails,
            bool containsDetails,
            int sourceDataItemIndex,
            ref int startSourceDataItemIndex,
            ref int endSourceDataItemIndex,
            ref bool stopVisit)
        {
            if (maxIndex < minIndex)
            {
                return;
            }

            int runningIndex = minIndex;

            sourceDataItemIndex += minIndex - itemsNode.CountDetailsBeforeGlobalIndex(minIndex);

            if (!containsDetails)
            {
                // If we contains no detail, we take a quick way out of it.
                if (startSourceDataItemIndex == -1)
                {
                    startSourceDataItemIndex = sourceDataItemIndex;
                }
                else
                {
                    if ((endSourceDataItemIndex + 1) != sourceDataItemIndex)
                    {
                        visitor.Visit(sourceContext, startSourceDataItemIndex, endSourceDataItemIndex, ref stopVisit);

                        if (stopVisit)
                        {
                            return;
                        }

                        startSourceDataItemIndex = sourceDataItemIndex;
                    }
                }

                endSourceDataItemIndex = sourceDataItemIndex + Math.Min(maxIndex - minIndex, itemsNode.Items.Count - 1);
                return;
            }

            int masterIndex;
            int detailStartIndex;
            int detailNodeIndex;

            while (runningIndex <= maxIndex)
            {
                DetailGeneratorNode detailNode = itemsNode.GetDetailNodeForIndex(runningIndex, out masterIndex, out detailStartIndex, out detailNodeIndex);

                if (detailNode != null)
                {
                    int detailEndIndex = Math.Min(detailNode.ItemCount - 1, detailStartIndex + (maxIndex - runningIndex));
                    sourceDataItemIndex -= detailStartIndex;
                    bool visitWasStopped;

                    (( IDataGridContextVisitable )detailNode.DetailGenerator).AcceptVisitor(
                        detailStartIndex, detailEndIndex, visitor, visitorType, visitDetails, out visitWasStopped);

                    stopVisit = stopVisit || visitWasStopped;

                    if (stopVisit)
                    {
                        break;
                    }

                    runningIndex += detailNode.ItemCount - detailStartIndex - 1;
                }
                else
                {
                    // set the first data index that will be visited for that items block
                    if (startSourceDataItemIndex == -1)
                    {
                        startSourceDataItemIndex = sourceDataItemIndex;
                        endSourceDataItemIndex   = sourceDataItemIndex;
                    }
                    else
                    {
                        if ((endSourceDataItemIndex + 1) != sourceDataItemIndex)
                        {
                            visitor.Visit(sourceContext, startSourceDataItemIndex, endSourceDataItemIndex, ref stopVisit);

                            if (stopVisit)
                            {
                                break;
                            }

                            startSourceDataItemIndex = sourceDataItemIndex;
                        }

                        endSourceDataItemIndex = sourceDataItemIndex;
                    }

                    sourceDataItemIndex++;
                }

                runningIndex++;
            }
        }
Example #12
0
        public void ProcessVisit(
            DataGridContext sourceContext,
            int minIndex,
            int maxIndex,
            IDataGridContextVisitor visitor,
            DataGridContextVisitorType visitorType,
            bool visitDetails,
            out bool visitWasStopped)
        {
            visitWasStopped = false;

            // This is used only for DataGridContextVisitorType.ItemsBlock
            int startSourceDataItemIndex = -1;
            int endSourceDataItemIndex   = -1;

            if (minIndex < 0)
            {
                throw new ArgumentException("The minimum index must be greater than or equal to zero.");
            }

            if ((visitorType & DataGridContextVisitorType.DataGridContext) == DataGridContextVisitorType.DataGridContext)
            {
                visitor.Visit(sourceContext, ref visitWasStopped);

                if (visitWasStopped)
                {
                    return;
                }
            }

            //Take a shortcut, if the visit is made only for contexts, and there is no child contexts
            //return right away.
            bool containsDetails = false;

            foreach (DataGridContext childContext in sourceContext.GetChildContexts())
            {
                containsDetails = true;
                break;
            }

            bool processed = false;

            do
            {
                //resets the flag that indicates if the node was already processed
                processed = false;

                int itemCount = this.CurrentNode.ItemCount;

                //If the whole current node is below the minIndex, jump over it.
                if ((this.Index + (itemCount - 1)) < minIndex)
                {
                    processed = true;
                }

                //when the index to visit exceeds the range defined, exit the loop.
                if (this.Index > maxIndex)
                {
                    break;
                }

                int minForNode = Math.Max(0, minIndex - this.Index);             // this will give the base offset within the node where to start the visitating!
                int maxForNode = Math.Min(itemCount - 1, maxIndex - this.Index); //this will five the max offset within this node to visit (protected against overlfow )

                if (!processed)
                {
                    HeadersFootersGeneratorNode headersNode = this.CurrentNode as HeadersFootersGeneratorNode;

                    if (headersNode != null)
                    {
                        bool isHeaderFooter = (headersNode.Parent == null);

                        //If the node is a Headers or Footers node AND the visitorType does not contain HeadersFooters
                        if ((isHeaderFooter) && ((visitorType & DataGridContextVisitorType.HeadersFooters) == DataGridContextVisitorType.HeadersFooters))
                        {
                            GeneratorNodeHelper.ProcessHeadersNodeVisit(headersNode, sourceContext, minForNode, maxForNode, visitor, ref visitWasStopped);
                        }
                        else if ((!isHeaderFooter) && ((visitorType & DataGridContextVisitorType.GroupHeadersFooters) == DataGridContextVisitorType.GroupHeadersFooters))
                        {
                            GeneratorNodeHelper.ProcessHeadersNodeVisit(headersNode, sourceContext, minForNode, maxForNode, visitor, ref visitWasStopped);
                        }

                        processed = true;
                    }
                }

                if (!processed)
                {
                    ItemsGeneratorNode itemsNode = this.CurrentNode as ItemsGeneratorNode;

                    if (itemsNode != null)
                    {
                        if ((visitorType & DataGridContextVisitorType.ItemsBlock) == DataGridContextVisitorType.ItemsBlock)
                        {
                            GeneratorNodeHelper.ProcessItemsNodeBlockVisit(
                                itemsNode, sourceContext,
                                minForNode, maxForNode,
                                visitor, visitorType, visitDetails, containsDetails, m_sourceDataIndex,
                                ref startSourceDataItemIndex, ref endSourceDataItemIndex, ref visitWasStopped);
                        }
                        else if (((visitDetails) && (containsDetails)) ||
                                 ((visitorType & DataGridContextVisitorType.Items) == DataGridContextVisitorType.Items))
                        {
                            GeneratorNodeHelper.ProcessItemsNodeVisit(
                                itemsNode, sourceContext,
                                minForNode, maxForNode,
                                visitor, visitorType, visitDetails, m_sourceDataIndex, ref visitWasStopped);
                        }

                        processed = true;
                    }
                }

                if (!processed)
                {
                    GroupGeneratorNode groupNode = this.CurrentNode as GroupGeneratorNode;

                    if (groupNode != null)
                    {
                        if ((visitorType & DataGridContextVisitorType.Groups) == DataGridContextVisitorType.Groups)
                        {
                            visitor.Visit(
                                sourceContext,
                                groupNode.CollectionViewGroup,
                                groupNode.NamesTree,
                                groupNode.Level,
                                groupNode.IsExpanded,
                                groupNode.IsComputedExpanded,
                                ref visitWasStopped);
                        }

                        processed = true;
                    }
                }

                if (!processed)
                {
                    throw new DataGridInternalException();
                }

                if (visitWasStopped)
                {
                    break;
                }

                if (this.MoveToChild())
                {
                    continue;
                }

                if (this.MoveToFollowing())
                {
                    continue;
                }

                break;
            }while(true); //loop is controled by continue and break statements.


            if ((visitorType & DataGridContextVisitorType.ItemsBlock) == DataGridContextVisitorType.ItemsBlock)
            {
                if (startSourceDataItemIndex != -1)
                {
                    bool stopVisit = false;
                    visitor.Visit(sourceContext, startSourceDataItemIndex, endSourceDataItemIndex, ref stopVisit);
                    visitWasStopped |= stopVisit;
                }
            }
        }
Example #13
0
        //FindItem skips over itemless nodes.
        public int FindItem(object item)
        {
            //finding items can only be done in "forward" direction
            int retval = -1;

            GeneratorNode originalNode            = m_currentNode;
            int           originalIndex           = m_index;
            int           originalSourceDataIndex = m_sourceDataIndex;

            while (retval == -1)
            {
                ItemsGeneratorNode itemsNode = m_currentNode as ItemsGeneratorNode;

                if (itemsNode != null)
                {
                    int tmpIndex = itemsNode.Items.IndexOf(item);
                    if (tmpIndex > -1)
                    {
                        tmpIndex += itemsNode.CountDetailsBeforeDataIndex(tmpIndex);
                        //item is directly from this items node... then return the appropriate index!
                        retval = m_index + tmpIndex;
                        break;
                    }
                    else
                    {
                        //if the item is from a detail, then I don't want to "use" it!!!
                        retval = -1;
                        //but continue looping.... to find occurances of this item somewhere else in the tree
                    }
                }
                else
                {
                    CollectionGeneratorNode collectionNode = m_currentNode as CollectionGeneratorNode;

                    if (collectionNode != null)
                    {
                        int tmpIndex = collectionNode.IndexOf(item);

                        if (tmpIndex != -1)
                        {
                            retval = m_index + tmpIndex;
                            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 (this.MoveToChild())
                {
                    continue;
                }

                //if we reach this point, it's because we have no child...
                if (this.MoveToNext())
                {
                    continue;
                }

                //final try, try "advancing" to the next item.
                if (this.MoveToFollowing())
                {
                    continue;
                }

                //up to this, we are in an endpoint, we failed.
                break;
            }

            if (retval == -1)
            {
                m_currentNode     = originalNode;
                m_index           = originalIndex;
                m_sourceDataIndex = originalSourceDataIndex;
            }

            return(retval);
        }
    public GeneratorNode CreateItemsGeneratorNode(
      IList collection,
      GeneratorNode parent,
      GeneratorNode previous,
      GeneratorNode next,
      CustomItemContainerGenerator generator )
    {
      Debug.Assert( collection != null, "collection cannot be null for CreateItemsGeneratorNode()" );
      Debug.Assert( generator != null );

      INotifyCollectionChanged notifyCollection = collection as INotifyCollectionChanged;

      Debug.Assert( notifyCollection != null, "collection must be a INotifyCollectionChanged for CreateItemsGeneratorNode()" );

      ItemCollection itemCollection = collection as ItemCollection;
      if( itemCollection != null )
      {
        DataGridCollectionView dgcv = itemCollection.SourceCollection as DataGridCollectionView;
        if( dgcv != null )
        {
          collection = dgcv;
        }
      }

      ItemsGeneratorNode node = new ItemsGeneratorNode( collection, parent );

      this.SetupCollectionGeneratorNode( node, parent, previous, next );


      node.AdjustLeafCount( node.Items.Count );

      return node;
    }
    private static void ProcessItemsNodeBlockVisit(
      ItemsGeneratorNode itemsNode,
      DataGridContext sourceContext,
      int minIndex,
      int maxIndex,
      IDataGridContextVisitor visitor,
      DataGridContextVisitorType visitorType,
      bool visitDetails,
      bool containsDetails,
      int sourceDataItemIndex,
      ref int startSourceDataItemIndex,
      ref int endSourceDataItemIndex,
      ref bool stopVisit )
    {
      if( maxIndex < minIndex )
        return;

      int runningIndex = minIndex;
      sourceDataItemIndex += minIndex - itemsNode.CountDetailsBeforeGlobalIndex( minIndex );

      if( !containsDetails )
      {
        // If we contains no detail, we take a quick way out of it.
        if( startSourceDataItemIndex == -1 )
        {
          startSourceDataItemIndex = sourceDataItemIndex;
        }
        else
        {
          if( ( endSourceDataItemIndex + 1 ) != sourceDataItemIndex )
          {
            visitor.Visit( sourceContext, startSourceDataItemIndex, endSourceDataItemIndex, ref stopVisit );

            if( stopVisit )
              return;

            startSourceDataItemIndex = sourceDataItemIndex;
          }
        }

        endSourceDataItemIndex = sourceDataItemIndex + Math.Min( maxIndex - minIndex, itemsNode.Items.Count - 1 );
        return;
      }

      int masterIndex;
      int detailStartIndex;
      int detailNodeIndex;

      while( runningIndex <= maxIndex )
      {
        DetailGeneratorNode detailNode = itemsNode.GetDetailNodeForIndex( runningIndex, out masterIndex, out detailStartIndex, out detailNodeIndex );

        if( detailNode != null )
        {
          int detailEndIndex = Math.Min( detailNode.ItemCount - 1, detailStartIndex + ( maxIndex - runningIndex ) );
          sourceDataItemIndex -= detailStartIndex;
          bool visitWasStopped;

          ( ( IDataGridContextVisitable )detailNode.DetailGenerator ).AcceptVisitor(
            detailStartIndex, detailEndIndex, visitor, visitorType, visitDetails, out visitWasStopped );

          stopVisit = stopVisit || visitWasStopped;

          if( stopVisit )
            break;

          runningIndex += detailNode.ItemCount - detailStartIndex - 1;
        }
        else
        {
          // set the first data index that will be visited for that items block
          if( startSourceDataItemIndex == -1 )
          {
            startSourceDataItemIndex = sourceDataItemIndex;
            endSourceDataItemIndex = sourceDataItemIndex;
          }
          else
          {
            if( ( endSourceDataItemIndex + 1 ) != sourceDataItemIndex )
            {
              visitor.Visit( sourceContext, startSourceDataItemIndex, endSourceDataItemIndex, ref stopVisit );

              if( stopVisit )
                break;

              startSourceDataItemIndex = sourceDataItemIndex;
            }

            endSourceDataItemIndex = sourceDataItemIndex;
          }

          sourceDataItemIndex++;
        }

        runningIndex++;
      }
    }
    private void RemoveDetails( ItemsGeneratorNode node, int nodeStartIndex, int nodeEndIndex, int replaceCount )
    {
      if( ( node.Details == null ) || ( node.Details.Count == 0 ) )
        return;

      int removeCount = nodeEndIndex - nodeStartIndex + 1 - replaceCount;
      //Note: If a replace was invoked, replace count will be greater than 0, and will be used to properly re-offset the 
      //details beyond the initial remove range.

      //Note2: for the case of a move or remove, the replace count must remain 0, so that the other details are correctly offseted.

      //first, create an array that will contain the keys for all the expanded details from the ItemsGeneratorNode
      int[] keys = new int[ node.Details.Count ];
      node.Details.Keys.CopyTo( keys, 0 );

      //sort the array, this will prevent any operation that will duplicate keys in the dictionary.
      Array.Sort<int>( keys );

      //cycle through all of the old items
      int countDetailsRemoved = 0;
      foreach( int key in keys )
      {
        //if the key is below the remove range, do not do anything with the dictionary entry

        //If the key match the remove range, remove the dictionary entry ( clear and queue remap )
        if( ( key >= nodeStartIndex ) && ( key <= nodeEndIndex ) )
        {
          List<DetailGeneratorNode> details;
          if( node.Details.TryGetValue( key, out details ) )
          {
            //sum them
            foreach( DetailGeneratorNode detailNode in details )
            {
              countDetailsRemoved += detailNode.ItemCount;
            }
            details.Clear(); //note: detail generators will be "closed" by another section of code (Remap floating details).

#if LOG
            Log.WriteLine( this, "details.Remove - IN" + node.GetHashCode().ToString() + " - Di" + key.ToString() );
#endif

            node.Details.Remove( key );

            if( node.Details.Count == 0 )
            {
              node.Details = null;
            }
          }
          else
          {
            //Key not found in the dictionary, something wrong is going on.
            throw new DataGridInternalException();
          }
        }
        //If the key is above the remove range, re-key it appropriatly.
        else if( key > nodeEndIndex )
        {
          List<DetailGeneratorNode> details;
          if( node.Details.TryGetValue( key, out details ) )
          {
#if LOG
            Log.WriteLine( this, "details.offset for remove - IN" + node.GetHashCode().ToString() + "- Di" + key.ToString() + " - new Di" + ( key - removeCount ).ToString() );
#endif
            node.Details.Remove( key );
            node.Details.Add( key - removeCount, details );
          }
          else
          {
            //Key not found in the dictionary, something wrong is going on.
            throw new DataGridInternalException();
          }
        }
      }

      //if some details have been "disconnected"
      if( countDetailsRemoved > 0 )
      {
        node.AdjustItemCount( -countDetailsRemoved );
      }
    }
    private static int ComputeDetailsCount( ItemsGeneratorNode node, int startIndex, int endIndex )
    {
      int detailCount = 0;

      if( node.Details != null )
      {
        //add the detail grids for the items into the calculation of what to remove.
        for( int i = startIndex; i <= endIndex; i++ )
        {
          List<DetailGeneratorNode> details;
          if( node.Details.TryGetValue( i, out details ) )
          {
            foreach( DetailGeneratorNode detailNode in details )
            {
              detailCount += detailNode.ItemCount;
            }
          }
        }
      }

      return detailCount;
    }
    public GeneratorNode CreateItemsGeneratorNode(
      IList collection,
      GeneratorNode parent,
      GeneratorNode previous,
      GeneratorNode next,
      CustomItemContainerGenerator generator )
    {
      Debug.Assert( collection != null, "collection cannot be null for CreateItemsGeneratorNode()" );
      Debug.Assert( generator != null );

      INotifyCollectionChanged notifyCollection = collection as INotifyCollectionChanged;

      Debug.Assert( notifyCollection != null, "collection must be a INotifyCollectionChanged for CreateItemsGeneratorNode()" );

      //case 113904: If the item source for the ItemsGeneratorNode is an ItemCollection, then
      //check if the underlying SourceCollection is a DataGridCollectionView.
      //This particular exception case is there to handle messaging quirks in the case 
      //of Master Detail edition. Refer to case for more details.
      ItemCollection itemCollection = collection as ItemCollection;
      if( itemCollection != null )
      {
        DataGridCollectionView dgcv = itemCollection.SourceCollection as DataGridCollectionView;
        if( dgcv != null )
        {
          collection = dgcv;
        }
      }

      ItemsGeneratorNode node = new ItemsGeneratorNode( collection, parent );

      this.SetupCollectionGeneratorNode( node, parent, previous, next );


      node.AdjustLeafCount( node.Items.Count );

      return node;
    }