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 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 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 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 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 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 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();
      }

    }
    public List<StickyContainerGenerated> GenerateStickyFooters(
      DependencyObject container,
      bool areFootersSticky,
      bool areGroupFootersSticky )
    {
      List<StickyContainerGenerated> generatedStickyContainers = new List<StickyContainerGenerated>();

      GeneratorNode containerNode;
      int containerRealizedIndex;
      object containerDataItem;


      if( this.FindGeneratorListMappingInformationForContainer( container,
                                                                out containerNode,
                                                                out containerRealizedIndex,
                                                                out containerDataItem ) )
      {
        GeneratorNodeHelper nodeHelper = null;
        DetailGeneratorNode detailNode = containerNode as DetailGeneratorNode;

        if( detailNode != null )
        {
          // Get the parent item of the detail node to be able
          // to readjust the containerRealizedIndex to the one
          // of the master item container since the detail node
          // will be processed by the DetailGenerator.
          containerDataItem = detailNode.DetailContext.ParentItem;

          // OPTIMIZATION: We will look in the m_genPos* first to avoid using
          //               FindItem for performance reason.
          int index = m_genPosToItem.IndexOf( containerDataItem );
          if( index > -1 )
          {
            int sourceDataIndex = ( int )m_genPosToContainer[ index ].GetValue( DataGridVirtualizingPanel.ItemIndexProperty );
            containerNode = m_genPosToNode[ index ];
            containerRealizedIndex = m_genPosToIndex[ index ];

            CollectionGeneratorNode collectionNode = containerNode as CollectionGeneratorNode;
            if( collectionNode != null )
            {
              nodeHelper = new GeneratorNodeHelper( containerNode, containerRealizedIndex - collectionNode.IndexOf( containerDataItem ), sourceDataIndex );
            }
          }

          if( nodeHelper == null )
          {
            // We want to find the ItemsGeneratorNode for the DetailNode.
            nodeHelper = new GeneratorNodeHelper( m_startNode, 0, 0 );
            containerRealizedIndex = nodeHelper.FindItem( containerDataItem );
            containerNode = nodeHelper.CurrentNode;
          }

          if( containerRealizedIndex == -1 )
            throw new DataGridInternalException();

          generatedStickyContainers.AddRange(
            this.GenerateStickyFootersForDetail( container, detailNode, areFootersSticky, areGroupFootersSticky ) );
        }
        else
        {
          CollectionGeneratorNode collectionNode = containerNode as CollectionGeneratorNode;
          if( collectionNode != null )
          {
            // We don't need to have an up to date sourceDataIndex so we pass 0
            nodeHelper = new GeneratorNodeHelper( containerNode, containerRealizedIndex - collectionNode.IndexOf( containerDataItem ), 0 );
          }

          if( nodeHelper == null )
          {
            nodeHelper = new GeneratorNodeHelper( containerNode, 0, 0 );
            nodeHelper.ReverseCalculateIndex();
          }
        }

        bool isHeaderNode =
          ( ( nodeHelper.CurrentNode is HeadersFootersGeneratorNode ) &&
            ( nodeHelper.CurrentNode.Previous == null ) );

        // We want to find the HeaderFooterGeneratorNode for the container 
        // node. This is to find the footers for the container.
        nodeHelper.MoveToEnd();
        HeadersFootersGeneratorNode footersNode = nodeHelper.CurrentNode as HeadersFootersGeneratorNode;

        if( !isHeaderNode )
        {
          // There is no footers to generate if the item count of the node is 0.
          if( footersNode.ItemCount > 0 )
          {
            if( ( ( areFootersSticky ) && ( footersNode.Parent == null ) )
              || ( ( areGroupFootersSticky ) && ( footersNode.Parent is GroupGeneratorNode ) ) )
            {
              generatedStickyContainers.AddRange(
                this.GenerateStickyFootersForNode( footersNode,
                                                   nodeHelper.Index,
                                                   containerRealizedIndex,
                                                   ( footersNode == containerNode ) ) );
            }
          }
        }

        // We must also find the bottom most footers for our level of detail and, if they need to be sticky,
        // we will generate the containers and add them the to list.
        HeadersFootersGeneratorNode bottomFootersNode = this.GetDetailFootersNode( nodeHelper );

        if( ( areFootersSticky )
          && ( bottomFootersNode != null )
          && ( bottomFootersNode != footersNode )
          && ( bottomFootersNode.ItemCount > 0 ) )
        {
          generatedStickyContainers.AddRange(
            this.GenerateStickyFootersForNode( bottomFootersNode, nodeHelper.Index ) );
        }
      }

      return generatedStickyContainers;
    }
    public int GetFirstHoldingContainerIndexForStickyFooter( DependencyObject stickyFooter )
    {
      int firstContainerIndex = 0;

      int containerRealizedIndex;
      GeneratorNode containerNode;
      object containerDataItem;

      if( this.FindGeneratorListMappingInformationForContainer( stickyFooter, out containerNode, out containerRealizedIndex, out containerDataItem ) )
      {
        DetailGeneratorNode detailNode = containerNode as DetailGeneratorNode;

        if( detailNode != null )
        {
          int detailIndex = this.FindGlobalIndexForDetailNode( detailNode );
          firstContainerIndex = detailNode.DetailGenerator.GetFirstHoldingContainerIndexForStickyFooter( stickyFooter ) + detailIndex;
        }
        else
        {
          GeneratorNodeHelper nodeHelper = null;

          CollectionGeneratorNode collectionNode = containerNode as CollectionGeneratorNode;
          if( collectionNode != null )
          {
            // We don't need to have an up to date sourceDataIndex so we pass 0
            nodeHelper = new GeneratorNodeHelper( containerNode, containerRealizedIndex - collectionNode.IndexOf( containerDataItem ), 0 );
          }

          if( nodeHelper == null )
          {
            nodeHelper = new GeneratorNodeHelper( containerNode, 0, 0 );
            nodeHelper.ReverseCalculateIndex();
          }

          nodeHelper.MoveToFirst();
          nodeHelper.MoveToNext(); // We exclude headers from this calculation.
          firstContainerIndex = nodeHelper.Index;
        }
      }

      return firstContainerIndex;
    }
    private int GetLastHoldingContainerIndexForStickyHeaderRecurse( DependencyObject stickyHeader, int parentCount )
    {
      int lastContainerIndex = 0;

      int containerRealizedIndex;
      GeneratorNode containerNode;
      object containerDataItem;

      if( this.FindGeneratorListMappingInformationForContainer( stickyHeader, out containerNode, out containerRealizedIndex, out containerDataItem ) )
      {
        DetailGeneratorNode detailNode = containerNode as DetailGeneratorNode;

        if( detailNode != null )
        {
          lastContainerIndex =
            detailNode.DetailGenerator.GetLastHoldingContainerIndexForStickyHeaderRecurse( stickyHeader, detailNode.ItemCount ) +
            this.FindGlobalIndexForDetailNode( detailNode );
        }
        else
        {
          ItemsGeneratorNode itemsNode = containerNode as ItemsGeneratorNode;

          if( itemsNode != null )
          {
            // This means that the sticky container is a MasterRow for a detail.
            if( this.AreDetailsExpanded( containerDataItem ) )
            {
              List<DetailGeneratorNode> detailNodesForDataItem = m_masterToDetails[ containerDataItem ];

              foreach( DetailGeneratorNode detailNodeForDataItem in detailNodesForDataItem )
              {
                lastContainerIndex +=
                  detailNodeForDataItem.ItemCount +
                  this.FindGlobalIndexForDetailNode( detailNodeForDataItem ) -
                  1;
              }
            }
          }
          else
          {
            GeneratorNodeHelper nodeHelper = null;

            // This means that the sticky container is a Header.
            CollectionGeneratorNode collectionNode = containerNode as CollectionGeneratorNode;
            if( collectionNode != null )
            {
              // We don't need to have an up to date sourceDataIndex so we pass 0
              nodeHelper = new GeneratorNodeHelper( containerNode, containerRealizedIndex - collectionNode.IndexOf( containerDataItem ), 0 );
            }

            if( nodeHelper == null )
            {
              nodeHelper = new GeneratorNodeHelper( containerNode, 0, 0 );
              nodeHelper.ReverseCalculateIndex();
            }

            lastContainerIndex = nodeHelper.Index;

            if( containerNode.Parent != null )
            {
              // This means that it is a GroupHeader
              lastContainerIndex += containerNode.Parent.ItemCount - 1;
            }
            else
            {
              lastContainerIndex += Math.Max( 0, parentCount - 1 );
            }
          }
        }
      }

      return lastContainerIndex;
    }