private PageIndexes CalculateVisibleIndexesForDesiredLastVisibleIndex( int desiredLastVisibleIndex ) { PageIndexes layoutedIndexes = new PageIndexes(); int stickyFooterCountForLastVisibleIndex = this.GetStickyFooterCountForIndex( desiredLastVisibleIndex ); layoutedIndexes.EndIndex = desiredLastVisibleIndex + stickyFooterCountForLastVisibleIndex; int minVisibleIndex = layoutedIndexes.EndIndex - this.CalculateFlooredPageItemCount( this.AnimatedScrollInfo.ViewportHeight ); layoutedIndexes.StartIndex = minVisibleIndex; // Process indexes from bottom to top up to an index for which // the number of required sticky headers will force a visible index // larger than the maximal acceptable last visible index for( int i = desiredLastVisibleIndex - 1; i >= minVisibleIndex; i-- ) { int stickyHeaderCountForIndex = this.GetStickyHeaderCountForIndex( i ); // If the index of the container - its sticky header count // is less than the minimal acceptable visible index, we ensure // the next container index as the minimal one to set as current if( minVisibleIndex > ( i - stickyHeaderCountForIndex ) ) { layoutedIndexes.StartIndex = i + 1; break; } } return layoutedIndexes; }
private void CalculateLayoutedIndexes( double verticalOffset, double viewportHeight, out PageIndexes layoutedIndexes ) { layoutedIndexes = new PageIndexes(); int itemCount = this.CustomItemContainerGenerator.ItemCount; if( itemCount > 0 ) { layoutedIndexes.StartIndex = Math.Min( this.GetContainerIndexFromOffset( verticalOffset, false ), itemCount - 1 ); layoutedIndexes.EndIndex = Math.Min( this.GetContainerIndexFromOffset( verticalOffset + viewportHeight - m_containerHeight, true ), itemCount - 1 ); layoutedIndexes.EndIndex = Math.Max( layoutedIndexes.StartIndex, layoutedIndexes.EndIndex ); } }
private PageIndexes CalculateVisibleIndexesForDesiredFirstVisibleIndex( int desiredFirstVisibleIndex ) { PageIndexes layoutedIndexes = new PageIndexes(); // Get the sticky header count for the desiredFirstVisibleIndex // and set the StartIndex int stickyHeaderCountForFirstVisibleIndex = this.GetStickyHeaderCountForIndex( desiredFirstVisibleIndex ); layoutedIndexes.StartIndex = desiredFirstVisibleIndex - stickyHeaderCountForFirstVisibleIndex; // Get the index of the maximal visible index according // to the number of container the viewport can display // and the number of sticky header the desiredFirstVisibleIndex // must display int maxLastVisibleIndex = layoutedIndexes.StartIndex + this.CalculateFlooredPageItemCount( this.AnimatedScrollInfo.ViewportHeight ); layoutedIndexes.EndIndex = maxLastVisibleIndex; // Process indexes from top to bottom up to an index for which // the number of required sticky footers will force a visible index // larger than the maximal acceptable last visible index for( int i = desiredFirstVisibleIndex + 1; i <= maxLastVisibleIndex; i++ ) { int stickyFooterCountForIndex = this.GetStickyFooterCountForIndex( i ); // The index of the container + its sticky footer count // is greater than the last acceptable visible index, we ensure // the previous container as the maximal one to set as current if( maxLastVisibleIndex < ( i + stickyFooterCountForIndex ) ) { layoutedIndexes.EndIndex = i - 1; break; } } return layoutedIndexes; }
private void GeneratePageAndUpdateIScrollInfoValues( Size availableSize, bool measureInvalidated, ref double viewportHeight ) { PageIndexes generatedPage; // We must ensure the VerticalOffset is valid according // to the actual viewport height in case the VerticalOffset // is greater than the new viewportHeight. IScrollInfo scrollInfo = this as IScrollInfo; double maxOffset = Math.Max( 0d, scrollInfo.ExtentHeight - 1 ); double offset = Math.Max( Math.Min( m_verticalOffset, maxOffset ), 0d ); if( offset != m_verticalOffset ) this.SetVerticalOffsetCore( offset ); int verticalOffset = ( int )m_verticalOffset; if( m_indexToBringIntoView != -1 ) { int intOffset = Math.Min( m_indexToBringIntoView, ( int )maxOffset ); if( intOffset < m_lastLayoutedPage.StartIndex ) { generatedPage = new PageIndexes( intOffset, -1 ); } else { generatedPage = new PageIndexes( -1, intOffset ); } } else { generatedPage = new PageIndexes( verticalOffset, -1 ); } // CALCULATE THE VIEWPORT HEIGHT AND GENERATE CONTAINERS this.GeneratePage( availableSize.Height, measureInvalidated, ref generatedPage, out viewportHeight ); // CALCULATE THE EXTENT WIDTH m_extentWidth = Math.Max( this.GetMaxDesiredWidth(), this.GetSynchronizedExtentWidth() ); // CALCULATE THE VIEWPORT WIDTH m_viewportWidth = Double.IsInfinity( availableSize.Width ) ? m_extentWidth : Math.Min( m_extentWidth, availableSize.Width ); this.SetVerticalOffsetCore( generatedPage.StartIndex ); this.InvalidateScrollInfo(); }
private void GenerateContainers( ICustomItemContainerGenerator generator, double pageHeight, HashSet<UIElement> layoutedContainersToRecycle, bool measureInvalidated, ref PageIndexes pageIndexes, out double containersHeight ) { int currentIndex = pageIndexes.StartIndex; GeneratorDirection direction; if( currentIndex == -1 ) { currentIndex = pageIndexes.EndIndex; Debug.Assert( currentIndex != -1 ); direction = GeneratorDirection.Backward; } else { direction = GeneratorDirection.Forward; } int startIndex = currentIndex; int endIndex = currentIndex; containersHeight = 0d; GeneratorPosition position; position = generator.GeneratorPositionFromIndex( currentIndex ); int itemCount = generator.ItemCount; using( generator.StartAt( position, direction, true ) ) { while( ( ( direction == GeneratorDirection.Forward ) ? ( currentIndex < itemCount ) : ( currentIndex >= 0 ) ) && ( pageHeight > 0 ) ) { UIElement container = this.GenerateContainer( generator, currentIndex, measureInvalidated ); if( container == null ) break; double containerHeight = container.DesiredSize.Height; m_layoutedContainers.Add( new LayoutedContainerInfo( currentIndex, container ) ); layoutedContainersToRecycle.Remove( container ); m_layoutedContainersToRecycle.Remove( container ); if( ( direction == GeneratorDirection.Backward ) && ( ( pageHeight - containerHeight ) < 0 ) ) { // We do not want to recycle the container since it will cause a re-invalidation of the measure and // may cause an infinit loop. This case has been observed with a MaxHeight set on the DataGridControl. break; } pageHeight -= containerHeight; containersHeight += containerHeight; endIndex = currentIndex; currentIndex += ( direction == GeneratorDirection.Forward ) ? 1 : -1; } } if( pageHeight > 0 ) { if( direction == GeneratorDirection.Forward ) { direction = GeneratorDirection.Backward; } else { direction = GeneratorDirection.Forward; } DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this ); if( ( direction == GeneratorDirection.Forward ) || ( ( dataGridContext == null ) || ( TableView.GetAutoFillLastPage( dataGridContext ) ) ) ) { currentIndex = ( direction == GeneratorDirection.Forward ) ? startIndex + 1 : startIndex - 1; if( ( direction == GeneratorDirection.Forward ) ? ( currentIndex < itemCount ) : ( currentIndex >= 0 ) ) { position = generator.GeneratorPositionFromIndex( currentIndex ); using( generator.StartAt( position, direction, true ) ) { // If we still have more space, try to get more container to fill up the page. while( ( ( direction == GeneratorDirection.Forward ) ? ( currentIndex < itemCount ) : ( currentIndex >= 0 ) ) && ( pageHeight > 0 ) ) { UIElement container = this.GenerateContainer( generator, currentIndex, measureInvalidated ); if( container == null ) break; double containerHeight = container.DesiredSize.Height; pageHeight -= containerHeight; m_layoutedContainers.Add( new LayoutedContainerInfo( currentIndex, container ) ); layoutedContainersToRecycle.Remove( container ); m_layoutedContainersToRecycle.Remove( container ); if( ( direction == GeneratorDirection.Backward ) && ( pageHeight < 0 ) ) { // We do not want to recycle the container since it will cause a re-invalidation of the measure and // may cause an infinit loop. This case has been observed with a MaxHeight set on the DataGridControl. break; } containersHeight += containerHeight; startIndex = currentIndex; currentIndex += ( direction == GeneratorDirection.Forward ) ? 1 : -1; } } } } } m_layoutedContainers.Sort(); if( startIndex > endIndex ) { pageIndexes = new PageIndexes( endIndex, startIndex ); } else { pageIndexes = new PageIndexes( startIndex, endIndex ); } }
private void GeneratePage( double availableHeight, bool measureInvalidated, ref PageIndexes generatedPage, out double containersHeight ) { containersHeight = 0d; ICustomItemContainerGenerator generator = this.CustomItemContainerGenerator; // The generator can be null if we're in design mode. if( generator == null ) return; // Make sure that container recycling is currently enabled on the generator. generator.IsRecyclingEnabled = true; bool pageChanged = ( generatedPage.StartIndex != m_lastGeneratedPage.StartIndex ) || ( m_lastGeneratedPageViewPortHeight != availableHeight ); if( ( pageChanged ) || ( measureInvalidated ) ) { UIElement focusedContainer = DataGridItemsHost.GetItemsHostContainerFromElement( this, Keyboard.FocusedElement as DependencyObject ); HashSet<UIElement> layoutedContainersToRecycle = new HashSet<UIElement>(); int newPageLengthApproximation = Math.Max( 1, m_lastGeneratedPage.Length ); foreach( LayoutedContainerInfo containerInfo in m_layoutedContainers ) { int realizedIndex = containerInfo.RealizedIndex; UIElement container = containerInfo.Container; bool waitForRecycle = ( ( generatedPage.StartIndex != -1 ) && ( realizedIndex >= generatedPage.StartIndex ) && ( realizedIndex <= generatedPage.StartIndex + newPageLengthApproximation ) ) || ( ( generatedPage.EndIndex != -1 ) && ( realizedIndex >= generatedPage.EndIndex - newPageLengthApproximation ) && ( realizedIndex <= generatedPage.EndIndex ) ); // Mark the container has a candidate for recycling. if( ( waitForRecycle ) || ( container == focusedContainer ) ) { layoutedContainersToRecycle.Add( container ); } // The element will probably not be on the generated page. Recycle its container immediatly // to minimize the number of new containers created. else { this.TrySafeRecycleContainer( generator, realizedIndex, container ); m_layoutedContainersToRecycle.Add( container ); } } m_layoutedContainers.Clear(); this.GenerateContainers( generator, availableHeight, layoutedContainersToRecycle, measureInvalidated, ref generatedPage, out containersHeight ); // We do not recycle the focused element! if( layoutedContainersToRecycle.Contains( focusedContainer ) ) { layoutedContainersToRecycle.Remove( focusedContainer ); m_layoutedContainersToRecycle.Remove( focusedContainer ); m_layoutedContainers.Add( new LayoutedContainerInfo( generator.GetRealizedIndexForContainer( focusedContainer ), focusedContainer ) ); } // Recycle the containers for the current page. this.RecycleContainers( layoutedContainersToRecycle, generator ); foreach( UIElement container in layoutedContainersToRecycle ) { m_layoutedContainersToRecycle.Add( container ); } m_lastGeneratedPage = generatedPage; m_lastGeneratedPageViewPortHeight = availableHeight; m_lastGeneratedPageContainersHeight = containersHeight; } else { generatedPage = m_lastGeneratedPage; containersHeight = m_lastGeneratedPageContainersHeight; } }
protected override void HandlePageDownKey( KeyEventArgs e ) { if( e.Handled ) return; e.Handled = true; DataGridControl dataGridControl = this.ParentDataGridControl; NavigationBehavior navigationBehavior = dataGridControl.NavigationBehavior; bool changeCurrentColumn = ( navigationBehavior == NavigationBehavior.CellOnly ); // CTRL + PageDown if( ( e.KeyboardDevice.Modifiers & ModifierKeys.Control ) == ModifierKeys.Control ) { // Simply scroll to bottom and set last index as current this.ScrollInfo.ScrollOwner.ScrollToBottom(); this.SetCurrent( this.CustomItemContainerGenerator.ItemCount - 1, changeCurrentColumn ); return; } UIElement focusedContainer = DataGridItemsHost.GetItemsHostContainerFromElement( this, Keyboard.FocusedElement as DependencyObject ); // No focused container or no navigation allowed if( ( focusedContainer == null ) || ( navigationBehavior == NavigationBehavior.None ) ) { // We just need to scroll one page down. this.ScrollInfo.ScrollOwner.PageDown(); return; } int generatorItemCount = this.CustomItemContainerGenerator.ItemCount; int focusedContainerRealizedIndex = this.CustomItemContainerGenerator.GetRealizedIndexForContainer( focusedContainer ); int maxIndex = generatorItemCount - 1; if( focusedContainerRealizedIndex == maxIndex ) { this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Down ) ); } else { this.InvalidateMeasure(); double containersHeight; PageIndexes generatedPage = new PageIndexes( focusedContainerRealizedIndex, -1 ); this.GeneratePage( this.RenderSize.Height, false, ref generatedPage, out containersHeight ); this.SetVerticalOffsetCore( generatedPage.StartIndex ); int initialDesiredIndex; // Last row not totally visible, take the one before the last if( ( containersHeight > this.RenderSize.Height ) && ( generatedPage.Length > 1 ) ) { initialDesiredIndex = generatedPage.EndIndex - 1; } else { initialDesiredIndex = generatedPage.EndIndex; } if( focusedContainerRealizedIndex != initialDesiredIndex ) { int desiredPageDownIndex = initialDesiredIndex; bool isDataRow = false; // SetCurrent on the index or up to focusedContainerRealizedIndex to a focusable index while( ( !dataGridControl.HasValidationError ) && ( !isDataRow ) && ( desiredPageDownIndex > focusedContainerRealizedIndex ) ) { if( this.SetCurrent( desiredPageDownIndex, changeCurrentColumn, out isDataRow ) ) return; desiredPageDownIndex--; } if( ( dataGridControl.HasValidationError ) || ( isDataRow ) ) return; //Debug.Assert( false, "When this will occur???" ); desiredPageDownIndex = initialDesiredIndex + 1; isDataRow = false; // No container were focused while processing indexes from focused to // initialDesiredIndex, try SetCurrent on indexes higher than // the initial down to maxIndex while( ( !dataGridControl.HasValidationError ) && ( !isDataRow ) && ( desiredPageDownIndex <= maxIndex ) ) { if( this.SetCurrent( desiredPageDownIndex, changeCurrentColumn, out isDataRow ) ) return; desiredPageDownIndex++; } } } }
protected override void HandlePageUpKey( KeyEventArgs e ) { if( e.Handled ) return; e.Handled = true; DataGridControl dataGridControl = this.ParentDataGridControl; NavigationBehavior navigationBehavior = dataGridControl.NavigationBehavior; bool changeCurrentColumn = ( navigationBehavior == NavigationBehavior.CellOnly ); if( ( e.KeyboardDevice.Modifiers & ModifierKeys.Control ) == ModifierKeys.Control ) { // Simply scroll to top and set first index as current this.ScrollInfo.ScrollOwner.ScrollToTop(); this.SetCurrent( 0, changeCurrentColumn ); return; } UIElement focusedContainer = DataGridItemsHost.GetItemsHostContainerFromElement( this, Keyboard.FocusedElement as DependencyObject ); if( ( focusedContainer == null ) || ( navigationBehavior == NavigationBehavior.None ) ) { // We just need to scroll one page up. this.ScrollInfo.ScrollOwner.PageUp(); return; } int focusedContainerRealizedIndex = this.CustomItemContainerGenerator.GetRealizedIndexForContainer( focusedContainer ); if( focusedContainerRealizedIndex == 0 ) { this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Up ) ); } else { this.InvalidateMeasure(); double containersHeight; PageIndexes generatedPage = new PageIndexes( -1, focusedContainerRealizedIndex ); this.GeneratePage( this.RenderSize.Height, false, ref generatedPage, out containersHeight ); this.SetVerticalOffsetCore( generatedPage.StartIndex ); int initialDesiredIndex = generatedPage.StartIndex; if( focusedContainerRealizedIndex != initialDesiredIndex ) { int desiredPageUpIndex = initialDesiredIndex; bool isDataRow = false; // SetCurrent on the index or down to a focusable index while( ( !dataGridControl.HasValidationError ) && ( !isDataRow ) && ( desiredPageUpIndex < focusedContainerRealizedIndex ) ) { if( this.SetCurrent( desiredPageUpIndex, changeCurrentColumn, out isDataRow ) ) return; desiredPageUpIndex++; } if( ( dataGridControl.HasValidationError ) || ( isDataRow ) ) return; // No container were focused while processing indexes from focused to // initialDesiredIndex, try SetCurrent on indexes lower than // the initial up to 0 desiredPageUpIndex = initialDesiredIndex - 1; isDataRow = false; while( ( !dataGridControl.HasValidationError ) && ( !isDataRow ) && ( desiredPageUpIndex > 0 ) ) { if( this.SetCurrent( desiredPageUpIndex, changeCurrentColumn, out isDataRow ) ) return; desiredPageUpIndex--; } } } }