public HierarchicalVirtualizationConstraints(VirtualizationCacheLength cacheLength,
     VirtualizationCacheLengthUnit cacheLengthUnit,
     Rect viewport)
 {
     _cacheLength = cacheLength;
     _cacheLengthUnit = cacheLengthUnit;
     _viewport = viewport;
 }
Example #2
0
 public HierarchicalVirtualizationConstraints(VirtualizationCacheLength cacheLength,
                                              VirtualizationCacheLengthUnit cacheLengthUnit,
                                              Rect viewport)
 {
     _cacheLength     = cacheLength;
     _cacheLengthUnit = cacheLengthUnit;
     _viewport        = viewport;
 }
Example #3
0
 public HierarchicalVirtualizationConstraints(VirtualizationCacheLength cacheLength,
                                              VirtualizationCacheLengthUnit cacheLengthUnit,
                                              Rect viewport)
 {
     _cacheLength      = cacheLength;
     _cacheLengthUnit  = cacheLengthUnit;
     _viewport         = viewport;
     _scrollGeneration = 0;  // internal field set separately by caller
 }
Example #4
0
 private void UpdateVirtualizationCacheLength()
 {
     if (Configs.FluencyFirst)
     {
         Resources["virtualizingPanelCacheLength"] = new VirtualizationCacheLength(100);
     }
     else
     {
         Resources["virtualizingPanelCacheLength"] = new VirtualizationCacheLength(800);
     }
 }
Example #5
0
        private void MeasureChild(
            ref IItemContainerGenerator generator,
            ref IContainItemStorage itemStorageProvider,
            ref object parentItem,
            ref bool hasUniformOrAverageContainerSizeBeenSet,
            ref double computedUniformOrAverageContainerSize,
            ref bool computedAreContainersUniformlySized,
            ref IList items,
            ref object item,
            ref IList children,
            ref int childIndex,
            ref bool visualOrderChanged,
            ref bool isHorizontal,
            ref Size childConstraint,
            ref Rect viewport,
            ref VirtualizationCacheLength cacheSize,
            ref VirtualizationCacheLengthUnit cacheUnit,
            ref bool foundFirstItemInViewport,
            ref double firstItemInViewportOffset,
            ref Size stackPixelSize,
            ref Size stackPixelSizeInViewport,
            ref Size stackPixelSizeInCacheBeforeViewport,
            ref Size stackPixelSizeInCacheAfterViewport,
            ref Size stackLogicalSize,
            ref Size stackLogicalSizeInViewport,
            ref Size stackLogicalSizeInCacheBeforeViewport,
            ref Size stackLogicalSizeInCacheAfterViewport,
            ref bool mustDisableVirtualization,
            bool isBeforeFirstItem,
            bool isAfterFirstItem,
            bool isAfterLastItem,
            bool skipActualMeasure,
            bool skipGeneration,
            ref bool hasBringIntoViewContainerBeenMeasured,
            ref bool hasVirtualizingChildren)
        {
            UIElement child = null;
            IHierarchicalVirtualizationAndScrollInfo virtualizingChild = null;
            Rect childViewport = Rect.Empty;
            VirtualizationCacheLength childCacheSize = new VirtualizationCacheLength(0.0);
            VirtualizationCacheLengthUnit childCacheUnit = VirtualizationCacheLengthUnit.Pixel;
            Size childDesiredSize = new Size();

            //
            // Get and connect the next child.
            //
            if (!skipActualMeasure && !skipGeneration)
            {
                bool newlyRealized;
                child = generator.GenerateNext(out newlyRealized) as UIElement;

                ItemContainerGenerator icg;
                if (child == null && (icg = generator as ItemContainerGenerator) != null)
                {
                    icg.Verify();
                }

                visualOrderChanged |= AddContainerFromGenerator(childIndex, child, newlyRealized, isBeforeFirstItem);
            }
            else
            {
                child = (UIElement)children[childIndex];
            }

            hasBringIntoViewContainerBeenMeasured |= (child == _bringIntoViewContainer);

            //
            // Set viewport constraints
            //

            bool isChildHorizontal = isHorizontal;
            virtualizingChild = GetVirtualizingChild(child, ref isChildHorizontal);

            SetViewportForChild(
                isHorizontal,
                itemStorageProvider,
                computedAreContainersUniformlySized,
                computedUniformOrAverageContainerSize,
                mustDisableVirtualization,
                child,
                virtualizingChild,
                item,
                isBeforeFirstItem,
                isAfterFirstItem,
                firstItemInViewportOffset,
                viewport,
                cacheSize,
                cacheUnit,
                stackPixelSize,
                stackPixelSizeInViewport,
                stackPixelSizeInCacheBeforeViewport,
                stackPixelSizeInCacheAfterViewport,
                stackLogicalSize,
                stackLogicalSizeInViewport,
                stackLogicalSizeInCacheBeforeViewport,
                stackLogicalSizeInCacheAfterViewport,
                out childViewport,
                ref childCacheSize,
                ref childCacheUnit);

            //
            // Measure the child
            //

            if (!skipActualMeasure)
            {
                child.Measure(childConstraint);
            }

            childDesiredSize = child.DesiredSize;

            //
            // Accumulate child size.
            //

            if (virtualizingChild != null)
            {
                //
                // Update the virtualizingChild once more to really be sure
                // that we can trust the Desired values from it. Previously
                // we may have bypassed some checks because the ItemsHost
                // wasn't connected.
                //
                virtualizingChild = GetVirtualizingChild(child, ref isChildHorizontal);

                mustDisableVirtualization |=
                    (virtualizingChild != null && virtualizingChild.MustDisableVirtualization) ||
                    isChildHorizontal != isHorizontal;
            }

            Size childPixelSize, childPixelSizeInViewport, childPixelSizeInCacheBeforeViewport, childPixelSizeInCacheAfterViewport;
            Size childLogicalSize, childLogicalSizeInViewport, childLogicalSizeInCacheBeforeViewport, childLogicalSizeInCacheAfterViewport;

            GetSizesForChild(
                isHorizontal,
                isChildHorizontal,
                isBeforeFirstItem,
                isAfterLastItem,
                virtualizingChild,
                childDesiredSize,
                childViewport,
                childCacheSize,
                childCacheUnit,
                out childPixelSize,
                out childPixelSizeInViewport,
                out childPixelSizeInCacheBeforeViewport,
                out childPixelSizeInCacheAfterViewport,
                out childLogicalSize,
                out childLogicalSizeInViewport,
                out childLogicalSizeInCacheBeforeViewport,
                out childLogicalSizeInCacheAfterViewport);

            UpdateStackSizes(
                isHorizontal,
                foundFirstItemInViewport,
                childPixelSize,
                childPixelSizeInViewport,
                childPixelSizeInCacheBeforeViewport,
                childPixelSizeInCacheAfterViewport,
                childLogicalSize,
                childLogicalSizeInViewport,
                childLogicalSizeInCacheBeforeViewport,
                childLogicalSizeInCacheAfterViewport,
                ref stackPixelSize,
                ref stackPixelSizeInViewport,
                ref stackPixelSizeInCacheBeforeViewport,
                ref stackPixelSizeInCacheAfterViewport,
                ref stackLogicalSize,
                ref stackLogicalSizeInViewport,
                ref stackLogicalSizeInCacheBeforeViewport,
                ref stackLogicalSizeInCacheAfterViewport);

            //
            // Cache the container size.
            //

            if (VirtualizingPanel.GetShouldCacheContainerSize(child))
            {
                SetContainerSizeForItem(
                    itemStorageProvider,
                    parentItem,
                    item,
                    IsPixelBased ? childPixelSize : childLogicalSize,
                    isHorizontal,
                    ref hasUniformOrAverageContainerSizeBeenSet,
                    ref computedUniformOrAverageContainerSize,
                    ref computedAreContainersUniformlySized);
            }

            if (virtualizingChild != null)
            {
                hasVirtualizingChildren = true;
            }
        }
Example #6
0
        private void MeasureExistingChildBeyondExtendedViewport(
            ref IItemContainerGenerator generator,
            ref IContainItemStorage itemStorageProvider,
            ref object parentItem,
            ref bool hasUniformOrAverageContainerSizeBeenSet,
            ref double computedUniformOrAverageContainerSize,
            ref bool computedAreContainersUniformlySized,
            ref IList items,
            ref IList children,
            ref int childIndex,
            ref bool visualOrderChanged,
            ref bool isHorizontal,
            ref Size childConstraint,
            ref bool foundFirstItemInViewport,
            ref double firstItemInViewportOffset,
            ref bool mustDisableVirtualization,
            ref bool hasVirtualizingChildren,
            ref bool hasBringIntoViewContainerBeenMeasured)
        {
            object item = ((ItemContainerGenerator)generator).ItemFromContainer((UIElement)children[childIndex]);
            Rect viewport = new Rect();
            VirtualizationCacheLength cacheSize = new VirtualizationCacheLength();
            VirtualizationCacheLengthUnit cacheUnit = VirtualizationCacheLengthUnit.Pixel;
            Size stackPixelSize = new Size();
            Size stackPixelSizeInViewport = new Size();
            Size stackPixelSizeInCacheBeforeViewport = new Size();
            Size stackPixelSizeInCacheAfterViewport = new Size();
            Size stackLogicalSize = new Size();
            Size stackLogicalSizeInViewport = new Size();
            Size stackLogicalSizeInCacheBeforeViewport = new Size();
            Size stackLogicalSizeInCacheAfterViewport = new Size();
            bool isBeforeFirstItem = childIndex < _firstItemInExtendedViewportChildIndex;
            bool isAfterFirstItem = childIndex > _firstItemInExtendedViewportChildIndex;
            bool isAfterLastItem = childIndex > _firstItemInExtendedViewportChildIndex + _actualItemsInExtendedViewportCount;
            bool skipActualMeasure = false;
            bool skipGeneration = true;

            MeasureChild(
                ref generator,
                ref itemStorageProvider,
                ref parentItem,
                ref hasUniformOrAverageContainerSizeBeenSet,
                ref computedUniformOrAverageContainerSize,
                ref computedAreContainersUniformlySized,
                ref items,
                ref item,
                ref children,
                ref childIndex,
                ref visualOrderChanged,
                ref isHorizontal,
                ref childConstraint,
                ref viewport,
                ref cacheSize,
                ref cacheUnit,
                ref foundFirstItemInViewport,
                ref firstItemInViewportOffset,
                ref stackPixelSize,
                ref stackPixelSizeInViewport,
                ref stackPixelSizeInCacheBeforeViewport,
                ref stackPixelSizeInCacheAfterViewport,
                ref stackLogicalSize,
                ref stackLogicalSizeInViewport,
                ref stackLogicalSizeInCacheBeforeViewport,
                ref stackLogicalSizeInCacheAfterViewport,
                ref mustDisableVirtualization,
                isBeforeFirstItem,
                isAfterFirstItem,
                isAfterLastItem,
                skipActualMeasure,
                skipGeneration,
                ref hasBringIntoViewContainerBeenMeasured,
                ref hasVirtualizingChildren);
        }
Example #7
0
        /// <summary>
        /// Returns the size of the child in pixel and logical units and also identifies the part of the child visible in the viewport.
        /// </summary>
        private void GetSizesForChild(
            bool isHorizontal,
            bool isChildHorizontal,
            bool isBeforeFirstItem,
            bool isAfterLastItem,
            IHierarchicalVirtualizationAndScrollInfo virtualizingChild,
            Size childDesiredSize,
            Rect childViewport,
            VirtualizationCacheLength childCacheSize,
            VirtualizationCacheLengthUnit childCacheUnit,
            out Size childPixelSize,
            out Size childPixelSizeInViewport,
            out Size childPixelSizeInCacheBeforeViewport,
            out Size childPixelSizeInCacheAfterViewport,
            out Size childLogicalSize,
            out Size childLogicalSizeInViewport,
            out Size childLogicalSizeInCacheBeforeViewport,
            out Size childLogicalSizeInCacheAfterViewport)
        {
            childPixelSize = new Size();
            childPixelSizeInViewport = new Size();
            childPixelSizeInCacheBeforeViewport = new Size();
            childPixelSizeInCacheAfterViewport = new Size();

            childLogicalSize = new Size();
            childLogicalSizeInViewport = new Size();
            childLogicalSizeInCacheBeforeViewport = new Size();
            childLogicalSizeInCacheAfterViewport = new Size();

            if (virtualizingChild != null)
            {
                RelativeHeaderPosition headerPosition = RelativeHeaderPosition.Top; // virtualizingChild.RelativeHeaderPosition;
                HierarchicalVirtualizationHeaderDesiredSizes headerDesiredSizes = virtualizingChild.HeaderDesiredSizes;
                HierarchicalVirtualizationItemDesiredSizes itemDesiredSizes = virtualizingChild.ItemDesiredSizes;

                Size pixelHeaderSize = headerDesiredSizes.PixelSize;
                Size logicalHeaderSize = headerDesiredSizes.LogicalSize;

                childPixelSize = childDesiredSize;

                if (headerPosition == RelativeHeaderPosition.Top || headerPosition == RelativeHeaderPosition.Bottom)
                {
                    childLogicalSize.Height = itemDesiredSizes.LogicalSize.Height + logicalHeaderSize.Height;
                    childLogicalSize.Width = Math.Max(itemDesiredSizes.LogicalSize.Width, logicalHeaderSize.Width);
                }
                else // if (headerPosition == RelativeHeaderPosition.Left || headerPosition == RelativeHeaderPosition.Right)
                {
                    childLogicalSize.Width = itemDesiredSizes.LogicalSize.Width + logicalHeaderSize.Width;
                    childLogicalSize.Height = Math.Max(itemDesiredSizes.LogicalSize.Height, logicalHeaderSize.Height);
                }

                if (IsPixelBased &&
                    ((isHorizontal && DoubleUtil.AreClose(itemDesiredSizes.PixelSize.Width, itemDesiredSizes.PixelSizeInViewport.Width)) ||
                    (!isHorizontal && DoubleUtil.AreClose(itemDesiredSizes.PixelSize.Height, itemDesiredSizes.PixelSizeInViewport.Height))))
                {
                    Rect childItemsViewport = childViewport;

                    if (headerPosition == RelativeHeaderPosition.Top || headerPosition == RelativeHeaderPosition.Left)
                    {
                        VirtualizationCacheLength childItemsCacheSize = childCacheSize;
                        VirtualizationCacheLengthUnit childItemsCacheUnit = childCacheUnit;

                        AdjustNonScrollingViewportForHeader(virtualizingChild, ref childItemsViewport, ref childItemsCacheSize, ref childItemsCacheUnit);
                    }

                    GetSizesForChildIntersectingTheViewport(
                        isHorizontal,
                        isChildHorizontal,
                        itemDesiredSizes.PixelSizeInViewport,
                        itemDesiredSizes.LogicalSizeInViewport,
                        childItemsViewport,
                        ref childPixelSizeInViewport,
                        ref childLogicalSizeInViewport,
                        ref childPixelSizeInCacheBeforeViewport,
                        ref childLogicalSizeInCacheBeforeViewport,
                        ref childPixelSizeInCacheAfterViewport,
                        ref childLogicalSizeInCacheAfterViewport);
                }
                else
                {
                    StackSizes(isHorizontal, ref childPixelSizeInViewport, itemDesiredSizes.PixelSizeInViewport);
                    StackSizes(isHorizontal, ref childLogicalSizeInViewport, itemDesiredSizes.LogicalSizeInViewport);
                }

                if (isChildHorizontal == isHorizontal)
                {
                    StackSizes(isHorizontal, ref childPixelSizeInCacheBeforeViewport, itemDesiredSizes.PixelSizeBeforeViewport);
                    StackSizes(isHorizontal, ref childLogicalSizeInCacheBeforeViewport, itemDesiredSizes.LogicalSizeBeforeViewport);
                    StackSizes(isHorizontal, ref childPixelSizeInCacheAfterViewport, itemDesiredSizes.PixelSizeAfterViewport);
                    StackSizes(isHorizontal, ref childLogicalSizeInCacheAfterViewport, itemDesiredSizes.LogicalSizeAfterViewport);
                }

                Rect childHeaderViewport = childViewport;
                Size childHeaderPixelSizeInViewport = new Size();
                Size childHeaderLogicalSizeInViewport = new Size();
                Size childHeaderPixelSizeInCacheBeforeViewport = new Size();
                Size childHeaderLogicalSizeInCacheBeforeViewport = new Size();
                Size childHeaderPixelSizeInCacheAfterViewport = new Size();
                Size childHeaderLogicalSizeInCacheAfterViewport = new Size();
                bool isChildHeaderHorizontal = (headerPosition == RelativeHeaderPosition.Left || headerPosition == RelativeHeaderPosition.Right);

                if (headerPosition == RelativeHeaderPosition.Bottom || headerPosition == RelativeHeaderPosition.Right)
                {
                    VirtualizationCacheLength childHeaderCacheSize = childCacheSize;
                    VirtualizationCacheLengthUnit childHeaderCacheUnit = childCacheUnit;

                    AdjustNonScrollingViewportForItems(virtualizingChild, ref childHeaderViewport, ref childHeaderCacheSize, ref childHeaderCacheUnit);
                }

                if (isBeforeFirstItem)
                {
                    childHeaderPixelSizeInCacheBeforeViewport = pixelHeaderSize;
                    childHeaderLogicalSizeInCacheBeforeViewport = logicalHeaderSize;
                }
                else if (isAfterLastItem)
                {
                    childHeaderPixelSizeInCacheAfterViewport = pixelHeaderSize;
                    childHeaderLogicalSizeInCacheAfterViewport = logicalHeaderSize;
                }
                else
                {
                    GetSizesForChildIntersectingTheViewport(
                        isHorizontal,
                        isChildHorizontal,
                        pixelHeaderSize,
                        logicalHeaderSize,
                        childHeaderViewport,
                        ref childHeaderPixelSizeInViewport,
                        ref childHeaderLogicalSizeInViewport,
                        ref childHeaderPixelSizeInCacheBeforeViewport,
                        ref childHeaderLogicalSizeInCacheBeforeViewport,
                        ref childHeaderPixelSizeInCacheAfterViewport,
                        ref childHeaderLogicalSizeInCacheAfterViewport);
                }

                StackSizes(isChildHeaderHorizontal, ref childPixelSizeInViewport, childHeaderPixelSizeInViewport);
                StackSizes(isChildHeaderHorizontal, ref childLogicalSizeInViewport, childHeaderLogicalSizeInViewport);
                StackSizes(isChildHeaderHorizontal, ref childPixelSizeInCacheBeforeViewport, childHeaderPixelSizeInCacheBeforeViewport);
                StackSizes(isChildHeaderHorizontal, ref childLogicalSizeInCacheBeforeViewport, childHeaderLogicalSizeInCacheBeforeViewport);
                StackSizes(isChildHeaderHorizontal, ref childPixelSizeInCacheAfterViewport, childHeaderPixelSizeInCacheAfterViewport);
                StackSizes(isChildHeaderHorizontal, ref childLogicalSizeInCacheAfterViewport, childHeaderLogicalSizeInCacheAfterViewport);
            }
            else
            {
                childPixelSize = childDesiredSize;
                childLogicalSize = new Size(DoubleUtil.GreaterThan(childPixelSize.Width, 0) ? 1 : 0,
                                            DoubleUtil.GreaterThan(childPixelSize.Height, 0) ? 1 : 0);

                if (isBeforeFirstItem)
                {
                    childPixelSizeInCacheBeforeViewport = childDesiredSize;
                    childLogicalSizeInCacheBeforeViewport = new Size(DoubleUtil.GreaterThan(childPixelSizeInCacheBeforeViewport.Width, 0) ? 1 : 0,
                                                                     DoubleUtil.GreaterThan(childPixelSizeInCacheBeforeViewport.Height, 0) ? 1 : 0);
                }
                else if (isAfterLastItem)
                {
                    childPixelSizeInCacheAfterViewport = childDesiredSize;
                    childLogicalSizeInCacheAfterViewport = new Size(DoubleUtil.GreaterThan(childPixelSizeInCacheAfterViewport.Width, 0) ? 1 : 0,
                                                                    DoubleUtil.GreaterThan(childPixelSizeInCacheAfterViewport.Height, 0) ? 1 : 0);
                }
                else
                {
                    GetSizesForChildIntersectingTheViewport(
                        isHorizontal,
                        isHorizontal,
                        childPixelSize,
                        childLogicalSize,
                        childViewport,
                        ref childPixelSizeInViewport,
                        ref childLogicalSizeInViewport,
                        ref childPixelSizeInCacheBeforeViewport,
                        ref childLogicalSizeInCacheBeforeViewport,
                        ref childPixelSizeInCacheAfterViewport,
                        ref childLogicalSizeInCacheAfterViewport);
                }
            }
        }
Example #8
0
        /// <summary>
        /// Called when to set a new viewport on the child when it is about to be measured.
        /// </summary>
        private void SetViewportForChild(
            bool isHorizontal,
            IContainItemStorage itemStorageProvider,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            bool mustDisableVirtualization,
            UIElement child,
            IHierarchicalVirtualizationAndScrollInfo virtualizingChild,
            object item,
            bool isBeforeFirstItem,
            bool isAfterFirstItem,
            double firstItemInViewportOffset,
            Rect parentViewport,
            VirtualizationCacheLength parentCacheSize,
            VirtualizationCacheLengthUnit parentCacheUnit,
            Size stackPixelSize,
            Size stackPixelSizeInViewport,
            Size stackPixelSizeInCacheBeforeViewport,
            Size stackPixelSizeInCacheAfterViewport,
            Size stackLogicalSize,
            Size stackLogicalSizeInViewport,
            Size stackLogicalSizeInCacheBeforeViewport,
            Size stackLogicalSizeInCacheAfterViewport,
            out Rect childViewport,
            ref VirtualizationCacheLength childCacheSize,
            ref VirtualizationCacheLengthUnit childCacheUnit)
        {
            childViewport = parentViewport;

            //
            // Adjust viewport offset for the child by deducting
            // the dimensions of the previous siblings.
            //
            if (isHorizontal)
            {
                if (isBeforeFirstItem)
                {
                    Size containerSize;
                    GetContainerSizeForItem(itemStorageProvider, item, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, out containerSize);
                    childViewport.X = (IsPixelBased ? stackPixelSizeInCacheBeforeViewport.Width : stackLogicalSizeInCacheBeforeViewport.Width) + containerSize.Width;
                    childViewport.Width = 0.0;
                }
                else if (isAfterFirstItem)
                {
                    childViewport.X = Math.Min(childViewport.X, 0) -
                                      (IsPixelBased ? stackPixelSizeInViewport.Width + stackPixelSizeInCacheAfterViewport.Width :
                                                       stackLogicalSizeInViewport.Width + stackLogicalSizeInCacheAfterViewport.Width);

                    childViewport.Width = Math.Max(childViewport.Width - stackPixelSizeInViewport.Width, 0.0);
                }
                else
                {
                    childViewport.X -= firstItemInViewportOffset;
                    childViewport.Width = Math.Max(childViewport.Width - stackPixelSizeInViewport.Width, 0.0);
                }

                if (parentCacheUnit == VirtualizationCacheLengthUnit.Item)
                {
                    childCacheSize = new VirtualizationCacheLength(
                        isAfterFirstItem || DoubleUtil.LessThanOrClose(childViewport.X, 0.0) ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheBeforeViewport - stackLogicalSizeInCacheBeforeViewport.Width, 0.0),
                        isBeforeFirstItem ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheAfterViewport - stackLogicalSizeInCacheAfterViewport.Width, 0.0));
                    childCacheUnit = VirtualizationCacheLengthUnit.Item;
                }
                else if (parentCacheUnit == VirtualizationCacheLengthUnit.Pixel)
                {
                    childCacheSize = new VirtualizationCacheLength(
                        isAfterFirstItem || DoubleUtil.LessThanOrClose(childViewport.X, 0.0) ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheBeforeViewport - stackPixelSizeInCacheBeforeViewport.Width, 0.0),
                        isBeforeFirstItem ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheAfterViewport - stackPixelSizeInCacheAfterViewport.Width, 0.0));
                    childCacheUnit = VirtualizationCacheLengthUnit.Pixel;
                }
            }
            else
            {
                if (isBeforeFirstItem)
                {
                    Size containerSize;
                    GetContainerSizeForItem(itemStorageProvider, item, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, out containerSize);
                    childViewport.Y = (IsPixelBased ? stackPixelSizeInCacheBeforeViewport.Height : stackLogicalSizeInCacheBeforeViewport.Height) + containerSize.Height;
                    childViewport.Height = 0.0;
                }
                else if (isAfterFirstItem)
                {
                    childViewport.Y = Math.Min(childViewport.Y, 0) -
                                      (IsPixelBased ? stackPixelSizeInViewport.Height + stackPixelSizeInCacheAfterViewport.Height :
                                                       stackLogicalSizeInViewport.Height + stackLogicalSizeInCacheAfterViewport.Height);

                    childViewport.Height = Math.Max(childViewport.Height - stackPixelSizeInViewport.Height, 0.0);
                }
                else
                {
                    childViewport.Y -= firstItemInViewportOffset;
                    childViewport.Height = Math.Max(childViewport.Height - stackPixelSizeInViewport.Height, 0.0);
                }

                if (parentCacheUnit == VirtualizationCacheLengthUnit.Item)
                {
                    childCacheSize = new VirtualizationCacheLength(
                        isAfterFirstItem || DoubleUtil.LessThanOrClose(childViewport.Y, 0.0) ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheBeforeViewport - stackLogicalSizeInCacheBeforeViewport.Height, 0.0),
                        isBeforeFirstItem ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheAfterViewport - stackLogicalSizeInCacheAfterViewport.Height, 0.0));
                    childCacheUnit = VirtualizationCacheLengthUnit.Item;
                }
                else if (parentCacheUnit == VirtualizationCacheLengthUnit.Pixel)
                {
                    childCacheSize = new VirtualizationCacheLength(
                        isAfterFirstItem || DoubleUtil.LessThanOrClose(childViewport.Y, 0.0) ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheBeforeViewport - stackPixelSizeInCacheBeforeViewport.Height, 0.0),
                        isBeforeFirstItem ?
                            0.0 :
                            Math.Max(parentCacheSize.CacheAfterViewport - stackPixelSizeInCacheAfterViewport.Height, 0.0));
                    childCacheUnit = VirtualizationCacheLengthUnit.Pixel;
                }
            }

            if (virtualizingChild != null)
            {
                virtualizingChild.Constraints = new HierarchicalVirtualizationConstraints(
                    childCacheSize,
                    childCacheUnit,
                    childViewport);
                virtualizingChild.InBackgroundLayout = MeasureCaches;
                virtualizingChild.MustDisableVirtualization = mustDisableVirtualization;
            }

            if (child is IHierarchicalVirtualizationAndScrollInfo)
            {
                //
                // Ensure that measure is invalid through the items panel
                // of the child, so it can react to the new viewport.
                //
                InvalidateMeasureOnItemsHost((IHierarchicalVirtualizationAndScrollInfo)child);
            }
        }
Example #9
0
        /// <summary>
        /// Returns the index of the first item visible (even partially) in the viewport.
        /// </summary>
        private void ComputeFirstItemInViewportIndexAndOffset(
            IList items,
            int itemCount,
            IContainItemStorage itemStorageProvider,
            Rect viewport,
            VirtualizationCacheLength cacheSize,
            bool isHorizontal,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            out double firstItemInViewportOffset,
            out int firstItemInViewportIndex,
            out bool foundFirstItemInViewport)
        {
            firstItemInViewportOffset = 0.0;
            firstItemInViewportIndex = 0;
            foundFirstItemInViewport = false;

            if (IsViewportEmpty(isHorizontal, viewport))
            {
                if (DoubleUtil.GreaterThan(cacheSize.CacheBeforeViewport, 0.0))
                {
                    firstItemInViewportIndex = itemCount-1;
                    ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, itemCount-1, out firstItemInViewportOffset);
                    foundFirstItemInViewport = true;
                }
                else
                {
                    //
                    // If the cacheSizeAfterViewport is also empty then we are merely
                    // here scouting to get a better measurement of this item.
                    //
                    firstItemInViewportIndex = 0;
                    firstItemInViewportOffset = 0;
                    foundFirstItemInViewport = DoubleUtil.GreaterThan(cacheSize.CacheAfterViewport, 0.0);
                }
            }
            else
            {
                //
                // Compute the span of this panel above the viewport. Note that if
                // the panel is below the viewport then this span is 0.0.
                //
                double spanBeforeViewport = Math.Max(isHorizontal ? viewport.X : viewport.Y, 0.0);

                if (areContainersUniformlySized)
                {
                    //
                    // This is an optimization for the case that all the children are of
                    // uniform dimension along the stacking axis. In this case the index
                    // and offset for the first item in the viewport is computed in constant time.
                    //
                    double childSize = uniformOrAverageContainerSize;
                    if (DoubleUtil.GreaterThan(childSize, 0))
                    {
                        firstItemInViewportIndex = (int)Math.Floor(spanBeforeViewport / childSize);
                        firstItemInViewportOffset = firstItemInViewportIndex * childSize;
                    }

                    foundFirstItemInViewport = (firstItemInViewportIndex < itemCount);
                    if (!foundFirstItemInViewport)
                    {
                        firstItemInViewportOffset = 0.0;
                        firstItemInViewportIndex = 0;
                    }
                }
                else
                {
                    if (DoubleUtil.AreClose(spanBeforeViewport, 0))
                    {
                        foundFirstItemInViewport = true;
                        firstItemInViewportOffset = 0.0;
                        firstItemInViewportIndex = 0;
                    }
                    else
                    {
                        Size containerSize;
                        double totalSpan = 0.0;      // total height or width in the stacking direction
                        double containerSpan = 0.0;

                        for (int i = 0; i < itemCount; i++)
                        {
                            object item = items[i];
                            GetContainerSizeForItem(itemStorageProvider, item, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, out containerSize);
                            containerSpan = isHorizontal ? containerSize.Width : containerSize.Height;
                            totalSpan += containerSpan;

                            if (DoubleUtil.GreaterThan(totalSpan, spanBeforeViewport))
                            {
                                //
                                // This is the first item that starts before the  viewport but ends after it.
                                // It is thus the the first item in the viewport.
                                //
                                firstItemInViewportIndex = i;
                                firstItemInViewportOffset = totalSpan - containerSpan;
                                break;
                            }
                        }

                        foundFirstItemInViewport = DoubleUtil.LessThan(spanBeforeViewport, totalSpan);
                        if (!foundFirstItemInViewport)
                        {
                            firstItemInViewportOffset = 0.0;
                            firstItemInViewportIndex = 0;
                        }
                    }
                }
            }
        }
Example #10
0
        }// *** END DEAD CODE ***

        /// <summary>
        /// Adjusts viewport to accommodate the inset.
        /// </summary>
        private void AdjustNonScrollingViewportForInset(
            bool isHorizontal,
            object parentItem,
            IContainItemStorage parentItemStorageProvider,
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            ref Rect viewport,
            ref VirtualizationCacheLength cacheLength,
            ref VirtualizationCacheLengthUnit cacheUnit)
        {
            // Recall that a viewport-rect has location (X,Y) expressed in scroll units
            // (pixels or items), but size (Width,Height) expressed in pixels and
            // describing the available size - previous contributions have been
            // deducted.
            Rect parentViewport = viewport;
            FrameworkElement container = virtualizationInfoProvider as FrameworkElement;
            Thickness inset = GetItemsHostInsetForChild(virtualizationInfoProvider, parentItemStorageProvider, parentItem);
            bool isHeaderBeforeItems = IsHeaderBeforeItems(isHorizontal, container, ref inset);
            double cacheBeforeSize = cacheLength.CacheBeforeViewport;
            double cacheAfterSize = cacheLength.CacheAfterViewport;

            // offset the viewport by the inset, along the scrolling axis
            if (isHorizontal)
            {
                viewport.X -= IsPixelBased ? inset.Left : isHeaderBeforeItems ? 1 : 0;
            }
            else
            {
                viewport.Y -= IsPixelBased ? inset.Top : isHeaderBeforeItems ? 1 : 0;
            }

            if (isHorizontal)
            {
                if (DoubleUtil.GreaterThan(parentViewport.X, 0))
                {
                    // Viewport is after start of this container

                    if (DoubleUtil.GreaterThan(viewport.Width, 0))
                    {
                        // Viewport is not yet full - we're delving for the first
                        // container in the viewport.  We're moving forward, so
                        // do not contribute to cache-after (we won't know whether
                        // this container needs to contribute to cache-after until
                        // after measuring this panel).

                        if (IsPixelBased && DoubleUtil.GreaterThan(0, viewport.X))
                        {
                            // Viewport starts within the leading inset

                            // The inset is split in two pieces by the viewport leading edge.
                            // The first piece contributes to the cache-before;
                            // its width is parentViewport.X
                            if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                            {
                                cacheBeforeSize = Math.Max(0, cacheBeforeSize - parentViewport.X);
                            }

                            // The second piece contributes to the viewport itself;
                            // its width is (inset.Left - parentViewport.X) = -viewport.X
                            viewport.Width = Math.Max(0, viewport.Width + viewport.X);
                        }
                        else
                        {
                            // Viewport starts after the leading inset.

                            // The contributions due to this container cannot be
                            // determined yet.  These are (a) leading inset to
                            // cache-before, (b) trailing inset to viewport and/or
                            // cache-after.
                        }
                    }
                    else
                    {
                        // viewport is full (and starts after this container).
                        // We're filling the cache-before back-to-front.

                        // The trailing inset contributes to cache-before
                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheBeforeSize = Math.Max(0, cacheBeforeSize - inset.Right);
                        }
                        else if (!isHeaderBeforeItems)
                        {
                            cacheBeforeSize = Math.Max(0, cacheBeforeSize - 1);
                        }
                    }
                }

                else if (DoubleUtil.GreaterThan(viewport.Width, 0))
                {
                    // Viewport has available space (and starts before this container)
                    // We are filling the viewport front-to-back.

                    if (DoubleUtil.GreaterThanOrClose(viewport.Width, inset.Left))
                    {
                        // Viewport has room for the entire leading inset.

                        // Leading inset contributes to viewport
                        viewport.Width = Math.Max(0, viewport.Width - inset.Left);
                    }
                    else
                    {
                        // Leading inset exhausts the remaining available space.

                        // The inset is split into two pieces (by the viewport trailing edge).
                        // The second piece contributes to the cache-after;
                        // its width is (inset.Left - viewport.Width)
                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheAfterSize = Math.Max(0, cacheAfterSize - (inset.Left - viewport.Width));
                        }

                        // The first piece contributes to the viewport itself;
                        // its width is viewport.Width (enough to decrease available width to zero)
                        viewport.Width = 0;
                    }
                }

                else
                {
                    // Viewport has no available space (and starts before this container).
                    // We are filling the cache-after front-to-back.

                    // The leading inset contributes to cache-after
                    if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                    {
                        cacheAfterSize = Math.Max(0, cacheAfterSize - inset.Left);
                    }
                    else if (isHeaderBeforeItems)
                    {
                        cacheAfterSize = Math.Max(0, cacheAfterSize - 1);
                    }
                }
            }
            else    // scroll axis is vertical
            {
                if (DoubleUtil.GreaterThan(parentViewport.Y, 0))
                {
                    // Viewport is after start of this container

                    if (DoubleUtil.GreaterThan(viewport.Height, 0))
                    {
                        // Viewport is not yet full - we're delving for the first
                        // container in the viewport.  We're moving forward, so
                        // do not contribute to cache-after (we won't know whether
                        // this container needs to contribute to cache-after until
                        // after measuring this panel).

                        if (IsPixelBased && DoubleUtil.GreaterThan(0, viewport.Y))
                        {
                            // Viewport starts within the leading inset

                            // The inset is split in two pieces by the viewport leading edge.
                            // The first piece contributes to the cache-before;
                            // its height is parentViewport.Y
                            if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                            {
                                cacheBeforeSize = Math.Max(0, cacheBeforeSize - parentViewport.Y);
                            }

                            // The second piece contributes to the viewport itself;
                            // its height is (inset.Top - parentViewport.Y) = -viewport.Y
                            viewport.Height = Math.Max(0, viewport.Height + viewport.Y);
                        }
                        else
                        {
                            // Viewport starts after the leading inset.

                            // The contributions due to this container cannot be
                            // determined yet.  These are (a) leading inset to
                            // cache-before, (b) trailing inset to viewport and/or
                            // cache-after.
                        }
                    }
                    else
                    {
                        // viewport is full (and starts after this container).
                        // We're filling the cache-before back-to-front.

                        // The trailing inset contributes to cache-before
                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheBeforeSize = Math.Max(0, cacheBeforeSize - inset.Bottom);
                        }
                        else if (!isHeaderBeforeItems)
                        {
                            cacheBeforeSize = Math.Max(0, cacheBeforeSize - 1);
                        }
                    }
                }

                else if (DoubleUtil.GreaterThan(viewport.Height, 0))
                {
                    // Viewport has available space (and starts before this container)
                    // We are filling the viewport front-to-back.

                    if (DoubleUtil.GreaterThanOrClose(viewport.Height, inset.Top))
                    {
                        // Viewport has room for the entire leading inset.

                        // Leading inset contributes to viewport
                        viewport.Height = Math.Max(0, viewport.Height - inset.Top);
                    }
                    else
                    {
                        // Leading inset exhausts the remaining available space.

                        // The inset is split into two pieces (by the viewport trailing edge).
                        // The second piece contributes to the cache-after;
                        // its height is (inset.Top - viewport.Height)
                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheAfterSize = Math.Max(0, cacheAfterSize - (inset.Top - viewport.Height));
                        }

                        // The first piece contributes to the viewport itself;
                        // its height is viewport.Height (enough to decrease available height to zero)
                        viewport.Height = 0;
                    }
                }

                else
                {
                    // Viewport has no available space (and starts before this container).
                    // We are filling the cache-after front-to-back.

                    // The leading inset contributes to cache-after
                    if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                    {
                        cacheAfterSize = Math.Max(0, cacheAfterSize - inset.Top);
                    }
                    else if (isHeaderBeforeItems)
                    {
                        cacheAfterSize = Math.Max(0, cacheAfterSize - 1);
                    }
                }
            }

            // apply the cache adjustment
            cacheLength = new VirtualizationCacheLength(cacheBeforeSize, cacheAfterSize);
        }
        /// <summary>
        /// Initializes the viewport for this panel.
        /// </summary>
        private void InitializeViewport(
            object parentItem,
            IContainItemStorage parentItemStorageProvider,
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            bool isHorizontal,
            Size constraint,
            ref Rect viewport,
            ref VirtualizationCacheLength cacheSize,
            ref VirtualizationCacheLengthUnit cacheUnit,
            out Rect extendedViewport)
        {

            Size extent = new Size();
            bool isVSP45Compat = IsVSP45Compat;

            if (IsScrolling)
            {
                //
                // We're the top level scrolling panel. Fetch the offset from the _scrollData.
                //

                Size size;
                double offsetX, offsetY;
                Size viewportSize;

                size = constraint;
                offsetX = _scrollData._offset.X;
                offsetY = _scrollData._offset.Y;
                extent = _scrollData._extent;
                viewportSize = _scrollData._viewport;

                if (!IsScrollActive || IgnoreMaxDesiredSize)
                {
                    _scrollData._maxDesiredSize = new Size();
                }

                if (IsPixelBased)
                {
                    viewport = new Rect(offsetX, offsetY, size.Width, size.Height);
                    CoerceScrollingViewportOffset(ref viewport, extent, isHorizontal);
                }
                else
                {
                    viewport = new Rect(offsetX, offsetY, viewportSize.Width, viewportSize.Height);
                    CoerceScrollingViewportOffset(ref viewport, extent, isHorizontal);
                    viewport.Size = size;
                }

                if (IsVirtualizing)
                {
                    cacheSize = VirtualizingStackPanel.GetCacheLength(this);
                    cacheUnit = VirtualizingStackPanel.GetCacheLengthUnit(this);

                    if (DoubleUtil.GreaterThan(cacheSize.CacheBeforeViewport, 0) ||
                        DoubleUtil.GreaterThan(cacheSize.CacheAfterViewport, 0))
                    {
                        if (!MeasureCaches)
                        {
                            WasLastMeasurePassAnchored = (_firstContainerInViewport != null) || (_bringIntoViewLeafContainer != null);

                            DispatcherOperation measureCachesOperation = MeasureCachesOperationField.GetValue(this);
                            if (measureCachesOperation == null)
                            {
                                Action measureCachesAction = null;
                                int retryCount = 3;
                                measureCachesAction = (Action)delegate()
                                    {
                                        Debug.Assert(retryCount >=0, "retry MeasureCaches too often");
                                        bool isLayoutDirty = (0 < retryCount--) && (MeasureDirty || ArrangeDirty);
                                        try
                                        {
                                            if (isVSP45Compat || !isLayoutDirty)
                                            {
                                                MeasureCachesOperationField.ClearValue(this);

                                                MeasureCaches = true;

                                                if (WasLastMeasurePassAnchored)
                                                {
                                                    SetAnchorInformation(isHorizontal);
                                                }

                                                InvalidateMeasure();
                                                UpdateLayout();
                                            }
                                        }
                                        finally
                                        {
                                            // check whether UpdateLayout finished the job
                                            isLayoutDirty = isLayoutDirty ||
                                                    ((0 < retryCount) && (MeasureDirty || ArrangeDirty));
                                            if (!isVSP45Compat && isLayoutDirty)
                                            {
                                                // try the measure-cache pass again later.
                                                // Note that we only do this when:
                                                // 1. this VSP's layout is dirty, either because
                                                //    a. it was dirty to begin with so we
                                                //       skipped UpdateLayout, or
                                                //    b. UpdateLayout ran, but left this VSP's
                                                //       layout dirty.
                                                // 2. we haven't run out of retries
                                                // 3. we're not in 4.5-compat mode
                                                //
                                                // (1) can happen if layout times out and moves to
                                                //     background.
                                                // (2) protects against loops when an app calls
                                                //     VSP.Measure directly, outside of the normal
                                                //     layout system (real appps don't do this,
                                                //     but test code does - it happens in the DrtXaml test).
                                                // (3) preserves compat with 4.5RTM, where the
                                                //     "move to background" situation led to an
                                                //     infinite loop.
                                                MeasureCachesOperationField.SetValue(this,
                                                    Dispatcher.BeginInvoke(DispatcherPriority.Background, measureCachesAction));
                                            }

                                            MeasureCaches = false;

                                            // If there is a pending anchor operation that got registered in
                                            // the current pass, or if layout didn't finish, we don't want to
                                            // clear the IsScrollActive flag.
                                            // We should allow that measure pass to also settle and then clear
                                            // the flag.

                                            DispatcherOperation anchoredInvalidateMeasureOperation = AnchoredInvalidateMeasureOperationField.GetValue(this);
                                            if (anchoredInvalidateMeasureOperation == null && (isVSP45Compat || !isLayoutDirty))
                                            {
                                                if (isVSP45Compat)
                                                {
                                                    IsScrollActive = false;
                                                }
                                                else if (IsScrollActive)
                                                {
                                                    // keep IsScrollActive set until the
                                                    // anchored measure has occurred.  It may
                                                    // need to remeasure, which should count
                                                    // as part of the scroll operation
                                                    DispatcherOperation clearIsScrollActiveOperation = ClearIsScrollActiveOperationField.GetValue(this);
                                                    if (clearIsScrollActiveOperation != null)
                                                    {
                                                        clearIsScrollActiveOperation.Abort();
                                                    }
                                                    clearIsScrollActiveOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                                        (Action)ClearIsScrollActive);

                                                    ClearIsScrollActiveOperationField.SetValue(this, clearIsScrollActiveOperation);
                                                }
                                            }
                                        }
                                    };
                                measureCachesOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                                                                measureCachesAction);
                                MeasureCachesOperationField.SetValue(this, measureCachesOperation);
                            }
                        }
                    }
                    else if (IsScrollActive)
                    {
                        DispatcherOperation clearIsScrollActiveOperation = ClearIsScrollActiveOperationField.GetValue(this);
                        if (clearIsScrollActiveOperation == null)
                        {
                            clearIsScrollActiveOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                (Action)ClearIsScrollActive);

                            ClearIsScrollActiveOperationField.SetValue(this, clearIsScrollActiveOperation);
                        }
                    }

                    NormalizeCacheLength(isHorizontal, viewport, ref cacheSize, ref cacheUnit);
                }
                else
                {
                    cacheSize = new VirtualizationCacheLength(
                        Double.PositiveInfinity,
                        IsViewportEmpty(isHorizontal, viewport) ?
                        0.0 :
                        Double.PositiveInfinity);
                    cacheUnit = VirtualizationCacheLengthUnit.Pixel;

                    ClearAsyncOperations();
                }
            }
            else if (virtualizationInfoProvider != null)
            {
                //
                // Adjust the viewport offset for a non scrolling panel to account for the HeaderSize
                // when virtualizing.
                //
                HierarchicalVirtualizationConstraints virtualizationConstraints = virtualizationInfoProvider.Constraints;
                viewport = virtualizationConstraints.Viewport;
                cacheSize = virtualizationConstraints.CacheLength;
                cacheUnit = virtualizationConstraints.CacheLengthUnit;
                MeasureCaches = virtualizationInfoProvider.InBackgroundLayout;

                if (isVSP45Compat)
                {
                    AdjustNonScrollingViewportForHeader(virtualizationInfoProvider, ref viewport, ref cacheSize, ref cacheUnit);
                }
                else
                {
                    AdjustNonScrollingViewportForInset(isHorizontal, parentItem, parentItemStorageProvider, virtualizationInfoProvider, ref viewport, ref cacheSize, ref cacheUnit);
                }
            }
            else
            {
                viewport = new Rect(0, 0, constraint.Width, constraint.Height);

                if (isHorizontal)
                {
                    viewport.Width = Double.PositiveInfinity;
                }
                else
                {
                    viewport.Height = Double.PositiveInfinity;
                }
            }

            // Adjust extendedViewport

            extendedViewport = _extendedViewport;

            if (isHorizontal)
            {
                extendedViewport.X += viewport.X - _viewport.X;
            }
            else
            {
                extendedViewport.Y += viewport.Y - _viewport.Y;
            }
        }
Example #12
0
        /// <summary>
        /// Extends the viewport to include the cacheSizeBeforeViewport and cacheSizeAfterViewport.
        /// </summary>
        private Rect ExtendViewport(
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            bool isHorizontal,
            Rect viewport,
            VirtualizationCacheLength cacheLength,
            VirtualizationCacheLengthUnit cacheUnit,
            Size stackPixelSizeInCacheBeforeViewport,
            Size stackLogicalSizeInCacheBeforeViewport,
            Size stackPixelSizeInCacheAfterViewport,
            Size stackLogicalSizeInCacheAfterViewport,
            Size stackPixelSize,
            Size stackLogicalSize,
            ref int itemsInExtendedViewportCount)
        {
            Debug.Assert(cacheUnit != VirtualizationCacheLengthUnit.Page, "Page cacheUnit is not expected here.");

            double pixelSize, pixelSizeBeforeViewport, pixelSizeAfterViewport;
            double logicalSize, logicalSizeBeforeViewport, logicalSizeAfterViewport;
            Rect extendedViewport = viewport;

            if (isHorizontal)
            {
                double approxSizeOfLogicalUnit = (DoubleUtil.GreaterThan(_previousStackPixelSizeInViewport.Width, 0.0) && DoubleUtil.GreaterThan(_previousStackLogicalSizeInViewport.Width, 0.0)) ?
                    _previousStackPixelSizeInViewport.Width / _previousStackLogicalSizeInViewport.Width : ScrollViewer._scrollLineDelta;

                pixelSize = stackPixelSize.Width;
                logicalSize = stackLogicalSize.Width;

                if (MeasureCaches)
                {
                    pixelSizeBeforeViewport = stackPixelSizeInCacheBeforeViewport.Width;
                    pixelSizeAfterViewport = stackPixelSizeInCacheAfterViewport.Width;
                    logicalSizeBeforeViewport = stackLogicalSizeInCacheBeforeViewport.Width;
                    logicalSizeAfterViewport = stackLogicalSizeInCacheAfterViewport.Width;
                }
                else
                {
                    pixelSizeBeforeViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheBeforeViewport * approxSizeOfLogicalUnit : cacheLength.CacheBeforeViewport;
                    pixelSizeAfterViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheAfterViewport * approxSizeOfLogicalUnit : cacheLength.CacheAfterViewport;
                    logicalSizeBeforeViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheBeforeViewport : cacheLength.CacheBeforeViewport / approxSizeOfLogicalUnit;
                    logicalSizeAfterViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheAfterViewport : cacheLength.CacheAfterViewport / approxSizeOfLogicalUnit;

                    if (IsPixelBased)
                    {
                        pixelSizeBeforeViewport = Math.Max(pixelSizeBeforeViewport, Math.Abs(_viewport.X - _extendedViewport.X));
                    }
                    else
                    {
                        logicalSizeBeforeViewport = Math.Max(logicalSizeBeforeViewport, Math.Abs(_viewport.X - _extendedViewport.X));
                    }
                }

                if (IsPixelBased)
                {
                    if (!IsScrolling && virtualizationInfoProvider != null &&
                        IsViewportEmpty(isHorizontal, extendedViewport) &&
                        DoubleUtil.GreaterThan(pixelSizeBeforeViewport, 0))
                    {
                        //
                        // If this is a GroupItem or a TreeViewItem that is completely above the viewport,
                        // then the CacheBeforeViewport allways designates the distance of the bottom of
                        // this panel from the top of the extendedViewport. Hence this computation for the offset.
                        //

                        extendedViewport.X = pixelSize - pixelSizeBeforeViewport;
                    }
                    else
                    {
                        extendedViewport.X -= pixelSizeBeforeViewport;
                    }

                    extendedViewport.Width += pixelSizeBeforeViewport + pixelSizeAfterViewport;

                    //
                    // Once again coerce the extended viewport dimensions to be within valid range.
                    //
                    if (IsScrolling)
                    {
                        if (DoubleUtil.LessThan(extendedViewport.X, 0.0))
                        {
                            extendedViewport.Width = Math.Max(extendedViewport.Width + extendedViewport.X, 0.0);
                            extendedViewport.X = 0.0;
                        }

                        if (DoubleUtil.GreaterThan(extendedViewport.X + extendedViewport.Width, _scrollData._extent.Width))
                        {
                            extendedViewport.Width = _scrollData._extent.Width - extendedViewport.X;
                        }
                    }
                }
                else
                {
                    if (!IsScrolling && virtualizationInfoProvider != null &&
                        IsViewportEmpty(isHorizontal, extendedViewport) &&
                        DoubleUtil.GreaterThan(pixelSizeBeforeViewport, 0))
                    {
                        //
                        // If this is a GroupItem or a TreeViewItem that is completely above the viewport,
                        // then the CacheBeforeViewport allways designates the distance of the bottom of
                        // this panel from the top of the extendedViewport. Hence this computation for the offset.
                        //

                        extendedViewport.X = logicalSize - logicalSizeBeforeViewport;
                    }
                    else
                    {
                        extendedViewport.X -= logicalSizeBeforeViewport;
                    }

                    extendedViewport.Width += pixelSizeBeforeViewport + pixelSizeAfterViewport;

                    if (IsScrolling)
                    {
                        if (DoubleUtil.LessThan(extendedViewport.X, 0.0))
                        {
                            extendedViewport.Width = Math.Max(extendedViewport.Width / approxSizeOfLogicalUnit + extendedViewport.X, 0.0) * approxSizeOfLogicalUnit;
                            extendedViewport.X = 0.0;
                        }

                        if (DoubleUtil.GreaterThan(extendedViewport.X + extendedViewport.Width / approxSizeOfLogicalUnit, _scrollData._extent.Width))
                        {
                            extendedViewport.Width = (_scrollData._extent.Width - extendedViewport.X) * approxSizeOfLogicalUnit;
                        }
                    }
                }
            }
            else
            {
                double approxSizeOfLogicalUnit = (DoubleUtil.GreaterThan(_previousStackPixelSizeInViewport.Height, 0.0) && DoubleUtil.GreaterThan(_previousStackLogicalSizeInViewport.Height, 0.0)) ?
                    _previousStackPixelSizeInViewport.Height / _previousStackLogicalSizeInViewport.Height : ScrollViewer._scrollLineDelta;

                pixelSize = stackPixelSize.Height;
                logicalSize = stackLogicalSize.Height;

                if (MeasureCaches)
                {
                    pixelSizeBeforeViewport = stackPixelSizeInCacheBeforeViewport.Height;
                    pixelSizeAfterViewport = stackPixelSizeInCacheAfterViewport.Height;
                    logicalSizeBeforeViewport = stackLogicalSizeInCacheBeforeViewport.Height;
                    logicalSizeAfterViewport = stackLogicalSizeInCacheAfterViewport.Height;
                }
                else
                {
                    pixelSizeBeforeViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheBeforeViewport * approxSizeOfLogicalUnit : cacheLength.CacheBeforeViewport;
                    pixelSizeAfterViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheAfterViewport * approxSizeOfLogicalUnit : cacheLength.CacheAfterViewport;
                    logicalSizeBeforeViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheBeforeViewport : cacheLength.CacheBeforeViewport / approxSizeOfLogicalUnit;
                    logicalSizeAfterViewport = (cacheUnit == VirtualizationCacheLengthUnit.Item) ? cacheLength.CacheAfterViewport : cacheLength.CacheAfterViewport / approxSizeOfLogicalUnit;

                    if (IsPixelBased)
                    {
                        pixelSizeBeforeViewport = Math.Max(pixelSizeBeforeViewport, Math.Abs(_viewport.Y - _extendedViewport.Y));
                    }
                    else
                    {
                        logicalSizeBeforeViewport = Math.Max(logicalSizeBeforeViewport, Math.Abs(_viewport.Y - _extendedViewport.Y));
                    }
                }

                if (IsPixelBased)
                {
                    if (!IsScrolling && virtualizationInfoProvider != null &&
                        IsViewportEmpty(isHorizontal, extendedViewport) &&
                        DoubleUtil.GreaterThan(pixelSizeBeforeViewport, 0))
                    {
                        //
                        // If this is a GroupItem or a TreeViewItem that is completely above the viewport,
                        // then the CacheBeforeViewport allways designates the distance of the bottom of
                        // this panel from the top of the extendedViewport. Hence this computation for the offset.
                        //

                        extendedViewport.Y = pixelSize - pixelSizeBeforeViewport;
                    }
                    else
                    {
                        extendedViewport.Y -= pixelSizeBeforeViewport;
                    }

                    extendedViewport.Height += pixelSizeBeforeViewport + pixelSizeAfterViewport;

                    //
                    // Once again coerce the extended viewport dimensions to be within valid range.
                    //
                    if (IsScrolling)
                    {
                        if (DoubleUtil.LessThan(extendedViewport.Y, 0.0))
                        {
                            extendedViewport.Height = Math.Max(extendedViewport.Height + extendedViewport.Y, 0.0);
                            extendedViewport.Y = 0.0;
                        }

                        if (DoubleUtil.GreaterThan(extendedViewport.Y + extendedViewport.Height, _scrollData._extent.Height))
                        {
                            extendedViewport.Height = _scrollData._extent.Height - extendedViewport.Y;
                        }
                    }
                }
                else
                {
                    if (!IsScrolling && virtualizationInfoProvider != null &&
                        IsViewportEmpty(isHorizontal, extendedViewport) &&
                        DoubleUtil.GreaterThan(pixelSizeBeforeViewport, 0))
                    {
                        //
                        // If this is a GroupItem or a TreeViewItem that is completely above the viewport,
                        // then the CacheBeforeViewport allways designates the distance of the bottom of
                        // this panel from the top of the extendedViewport. Hence this computation for the offset.
                        //

                        extendedViewport.Y = logicalSize - logicalSizeBeforeViewport;
                    }
                    else
                    {
                        extendedViewport.Y -= logicalSizeBeforeViewport;
                    }

                    extendedViewport.Height += pixelSizeBeforeViewport + pixelSizeAfterViewport;

                    if (IsScrolling)
                    {
                        if (DoubleUtil.LessThan(extendedViewport.Y, 0.0))
                        {
                            extendedViewport.Height = Math.Max(extendedViewport.Height / approxSizeOfLogicalUnit + extendedViewport.Y, 0.0) * approxSizeOfLogicalUnit;
                            extendedViewport.Y = 0.0;
                        }

                        if (DoubleUtil.GreaterThan(extendedViewport.Y + extendedViewport.Height / approxSizeOfLogicalUnit, _scrollData._extent.Height))
                        {
                            extendedViewport.Height = (_scrollData._extent.Height - extendedViewport.Y) * approxSizeOfLogicalUnit;
                        }
                    }
                }
            }

            if (MeasureCaches)
            {
                itemsInExtendedViewportCount = _actualItemsInExtendedViewportCount;
            }
            else
            {
                double factor = Math.Max(1.0, isHorizontal ? extendedViewport.Width / viewport.Width : extendedViewport.Height / viewport.Height);
                int calcItemsInExtendedViewportCount = (int)Math.Ceiling(factor * _actualItemsInExtendedViewportCount);
                itemsInExtendedViewportCount = Math.Max(calcItemsInExtendedViewportCount, itemsInExtendedViewportCount);
            }

            return extendedViewport;
        }
Example #13
0
        private void NormalizeCacheLength(
            bool isHorizontal,
            Rect viewport,
            ref VirtualizationCacheLength cacheLength,
            ref VirtualizationCacheLengthUnit cacheUnit)
        {
            if (cacheUnit == VirtualizationCacheLengthUnit.Page)
            {
                double factor = isHorizontal ? viewport.Width : viewport.Height;

                if (Double.IsPositiveInfinity(factor))
                {
                    cacheLength = new VirtualizationCacheLength(
                        0,
                        0);
                }
                else
                {
                    cacheLength = new VirtualizationCacheLength(
                        cacheLength.CacheBeforeViewport * factor,
                        cacheLength.CacheAfterViewport * factor);
                }

                cacheUnit = VirtualizationCacheLengthUnit.Pixel;
            }
        }
Example #14
0
        /// <summary>
        /// Initializes the viewport for this panel.
        /// </summary>
        private void InitializeViewport(
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            bool isHorizontal,
            Size constraint,
            ref Rect viewport,
            ref VirtualizationCacheLength cacheSize,
            ref VirtualizationCacheLengthUnit cacheUnit,
            out Rect extendedViewport)
        {

            Size extent = new Size();

            if (IsScrolling)
            {
                //
                // We're the top level scrolling panel. Fetch the offset from the _scrollData.
                //

                Size size;
                double offsetX, offsetY;
                Size viewportSize;

                size = constraint;
                offsetX = _scrollData._offset.X;
                offsetY = _scrollData._offset.Y;
                extent = _scrollData._extent;
                viewportSize = _scrollData._viewport;

                if (!IsScrollActive || IgnoreMaxDesiredSize)
                {
                    _scrollData._maxDesiredSize = new Size();
                }

                if (IsPixelBased)
                {
                    viewport = new Rect(offsetX, offsetY, size.Width, size.Height);
                    CoerceScrollingViewportOffset(ref viewport, extent, isHorizontal);
                }
                else
                {
                    viewport = new Rect(offsetX, offsetY, viewportSize.Width, viewportSize.Height);
                    CoerceScrollingViewportOffset(ref viewport, extent, isHorizontal);
                    viewport.Size = size;
                }

                if (IsVirtualizing)
                {
                    cacheSize = VirtualizingStackPanel.GetCacheLength(this);
                    cacheUnit = VirtualizingStackPanel.GetCacheLengthUnit(this);

                    if (DoubleUtil.GreaterThan(cacheSize.CacheBeforeViewport, 0) ||
                        DoubleUtil.GreaterThan(cacheSize.CacheAfterViewport, 0))
                    {
                        if (!MeasureCaches)
                        {
                            WasLastMeasurePassAnchored = (_firstContainerInViewport != null) || (_bringIntoViewLeafContainer != null);

                            DispatcherOperation measureCachesOperation = MeasureCachesOperationField.GetValue(this);
                            if (measureCachesOperation == null)
                            {
                                measureCachesOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                    (Action)delegate()
                                    {
                                        try
                                        {
                                            MeasureCachesOperationField.ClearValue(this);

                                            MeasureCaches = true;

                                            if (WasLastMeasurePassAnchored)
                                            {
                                                SetAnchorInformation(isHorizontal);
                                            }

                                            InvalidateMeasure();
                                            UpdateLayout();
                                        }
                                        finally
                                        {
                                            MeasureCaches = false;

                                            // If there is a pending anchor operation that got registered in
                                            // the current pass, we dont want to clear the IsScrollActive flag.
                                            // We should allow that measure pass to also settle and then clear
                                            // the flag.

                                            DispatcherOperation anchoredInvalidateMeasureOperation = AnchoredInvalidateMeasureOperationField.GetValue(this);
                                            if (anchoredInvalidateMeasureOperation == null)
                                            {
                                                IsScrollActive = false;
                                            }
                                        }
                                    });

                                MeasureCachesOperationField.SetValue(this, measureCachesOperation);
                            }
                        }
                    }
                    else if (IsScrollActive)
                    {
                        DispatcherOperation clearIsScrollActiveOperation = ClearIsScrollActiveOperationField.GetValue(this);
                        if (clearIsScrollActiveOperation == null)
                        {
                            clearIsScrollActiveOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                (Action)delegate()
                                {
                                    ClearIsScrollActiveOperationField.ClearValue(this);
                                    IsScrollActive = false;
                                });

                            ClearIsScrollActiveOperationField.SetValue(this, clearIsScrollActiveOperation);
                        }
                    }

                    NormalizeCacheLength(isHorizontal, viewport, ref cacheSize, ref cacheUnit);
                }
                else
                {
                    cacheSize = new VirtualizationCacheLength(
                        Double.PositiveInfinity,
                        IsViewportEmpty(isHorizontal, viewport) ?
                        0.0 :
                        Double.PositiveInfinity);
                    cacheUnit = VirtualizationCacheLengthUnit.Pixel;

                    ClearAsyncOperations();
                }
            }
            else if (virtualizationInfoProvider != null)
            {
                //
                // Adjust the viewport offset for a non scrolling panel to account for the HeaderSize
                // when virtualizing.
                //
                HierarchicalVirtualizationConstraints virtualizationConstraints = virtualizationInfoProvider.Constraints;
                viewport = virtualizationConstraints.Viewport;
                cacheSize = virtualizationConstraints.CacheLength;
                cacheUnit = virtualizationConstraints.CacheLengthUnit;
                MeasureCaches = virtualizationInfoProvider.InBackgroundLayout;

                AdjustNonScrollingViewportForHeader(virtualizationInfoProvider, ref viewport, ref cacheSize, ref cacheUnit);
            }
            else
            {
                viewport = new Rect(0, 0, constraint.Width, constraint.Height);

                if (isHorizontal)
                {
                    viewport.Width = Double.PositiveInfinity;
                }
                else
                {
                    viewport.Height = Double.PositiveInfinity;
                }
            }

            // Adjust extendedViewport

            extendedViewport = _extendedViewport;

            if (isHorizontal)
            {
                extendedViewport.X += viewport.X - _viewport.X;
            }
            else
            {
                extendedViewport.Y += viewport.Y - _viewport.Y;
            }
        }
Example #15
0
        private Size MeasureOverrideImpl(Size constraint, double? lastPageSafeOffset, ref List<double> previouslyMeasuredOffsets, bool remeasure)
        {
            bool etwTracingEnabled = IsScrolling && EventTrace.IsEnabled(EventTrace.Keyword.KeywordGeneral, EventTrace.Level.Info);
            if (etwTracingEnabled)
            {
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientStringBegin, EventTrace.Keyword.KeywordGeneral, EventTrace.Level.Info, "VirtualizingStackPanel :MeasureOverride");
            }

            //
            //  Initialize the sizes to be computed in this routine.
            //
            Size stackPixelSize = new Size();
            Size stackLogicalSize = new Size();
            Size stackPixelSizeInViewport = new Size();
            Size stackLogicalSizeInViewport = new Size();
            Size stackPixelSizeInCacheBeforeViewport = new Size();
            Size stackLogicalSizeInCacheBeforeViewport = new Size();
            Size stackPixelSizeInCacheAfterViewport = new Size();
            Size stackLogicalSizeInCacheAfterViewport = new Size();

            bool hasVirtualizingChildren = false;
            ItemsChangedDuringMeasure = false;

            try
            {
                if (!IsItemsHost)
                {
                    stackPixelSize = MeasureNonItemsHost(constraint);
                }
                else
                {
                    // ===================================================================================
                    // ===================================================================================
                    // Fetch owners
                    // ===================================================================================
                    // ===================================================================================

                    ItemsControl itemsControl = null;
                    GroupItem groupItem = null;

                    //
                    // This is an interface implemented by the owner of this panel in order to facilitate between a parent
                    // panel and this one when virtualizing in a hierarchy. (Eg. TreeView or grouping ItemsControl.) This
                    // interface is currently implemented by TreeViewItem and GroupItem.
                    //
                    IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider = null;

                    //
                    // This is a service provided by the owner of this panel to store and retrieve information on a per item
                    // basis. Specifically this panel uses this service to remember DesiredSize of items when virtualizing.
                    // This interface is currently implemented by ItemsControl and GroupItem.
                    //
                    IContainItemStorage itemStorageProvider = null;

                    //
                    // This is the item representing the owner for this panel. (Eg. The CollectionViewGroup for the owner GroupItem)
                    //
                    object parentItem = null;

                    //
                    // Is horizontally stacking
                    //
                    bool isHorizontal = (Orientation == Orientation.Horizontal);

                    //
                    // Compute if this panel is different in orientation that either its parent or descendents
                    //
                    bool mustDisableVirtualization = false;

                    //
                    // Fetch the owner for this panel. That could either be an ItemsControl or a GroupItem.
                    //
                    GetOwners(true /*shouldSetVirtualizationState*/, isHorizontal, out itemsControl, out groupItem, out itemStorageProvider, out virtualizationInfoProvider, out parentItem, out mustDisableVirtualization);

                    // ===================================================================================
                    // ===================================================================================
                    // Initialize viewport
                    // ===================================================================================
                    // ===================================================================================

                    //
                    // The viewport constraint used by this panel.
                    //
                    Rect viewport = Rect.Empty, extendedViewport = Rect.Empty;

                    //
                    // Sizes of cache before/after viewport
                    //
                    VirtualizationCacheLength cacheSize = new VirtualizationCacheLength(0.0);
                    VirtualizationCacheLengthUnit cacheUnit = VirtualizationCacheLengthUnit.Pixel;

                    //
                    // Initialize the viewport for this panel.
                    //
                    InitializeViewport(virtualizationInfoProvider, isHorizontal, constraint, ref viewport, ref cacheSize, ref cacheUnit, out extendedViewport);

                    // ===================================================================================
                    // ===================================================================================
                    // Compute first item in viewport
                    // ===================================================================================
                    // ===================================================================================

                    //
                    // Index of first item in the viewport.
                    //
                    int firstItemInViewportIndex = Int32.MinValue, lastItemInViewportIndex = Int32.MaxValue, firstItemInViewportChildIndex = Int32.MinValue, firstItemInExtendedViewportIndex = Int32.MinValue;

                    //
                    // Offset of the top of the first item relative to the top of the viewport.
                    //
                    double firstItemInViewportOffset = 0.0, firstItemInExtendedViewportOffset = 0.0;

                    //
                    // Says if the first and last items in the viewport has been encountered this far
                    //
                    bool foundFirstItemInViewport = false, foundLastItemInViewport = false, foundFirstItemInExtendedViewport = false;

                    //
                    // Get set to enumerate the items of the owner
                    //
                    EnsureGenerator();
                    IList children = RealizedChildren;  // yes, this is weird, but this property ensures the Generator is properly initialized.
                    IItemContainerGenerator generator = Generator;
                    IList items = ((ItemContainerGenerator)generator).ItemsInternal;
                    int itemCount = items.Count;

                    //
                    // Locally cache the values of this flag and size until the end of this measure pass.
                    // This is important because switching the areContainersUniformlySized flag in the
                    // middle of a measure leads to skewed results in the computation of the extensions
                    // to the stackSize.
                    //
                    bool areContainersUniformlySized = GetAreContainersUniformlySized(itemStorageProvider, parentItem);
                    bool computedAreContainersUniformlySized = areContainersUniformlySized;
                    bool hasUniformOrAverageContainerSizeBeenSet;
                    double uniformOrAverageContainerSize = GetUniformOrAverageContainerSize(itemStorageProvider, parentItem, out hasUniformOrAverageContainerSizeBeenSet);
                    double computedUniformOrAverageContainerSize = uniformOrAverageContainerSize;

                    //
                    // Compute index and offset of first item in the viewport
                    //
                    ComputeFirstItemInViewportIndexAndOffset(items, itemCount, itemStorageProvider, viewport, cacheSize,
                        isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize,
                        out firstItemInViewportOffset,
                        out firstItemInViewportIndex,
                        out foundFirstItemInViewport);

                    //
                    // Compute index and offset of first item in the extendedViewport
                    //
                    ComputeFirstItemInViewportIndexAndOffset(items, itemCount, itemStorageProvider, extendedViewport, new VirtualizationCacheLength(0.0),
                        isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize,
                        out firstItemInExtendedViewportOffset,
                        out firstItemInExtendedViewportIndex,
                        out foundFirstItemInExtendedViewport);

                    if (IsVirtualizing)
                    {
                        // ===================================================================================
                        // ===================================================================================
                        // Recycle containers
                        // ===================================================================================
                        // ===================================================================================

                        //
                        // If we arrive here through a remeasure this assertion wont be true
                        // because we have postponed the cleanup until after this remeasure
                        // is complete.
                        //
                        // debug_AssertRealizedChildrenEqualVisualChildren();

                        //
                        // If recycling clean up before generating children so that recycled
                        // containers are available for the current measure pass.
                        // But if this is a remeasure pass we do not want to recycle the
                        // containers because there could be several iterations of these and
                        // we should wait for the storm to subside to reclaim containers.
                        //
                        if (!remeasure && InRecyclingMode)
                        {
                            CleanupContainers(firstItemInExtendedViewportIndex, _itemsInExtendedViewportCount, itemsControl);
                            debug_VerifyRealizedChildren();
                        }
                    }

                    // ===================================================================================
                    // ===================================================================================
                    // Initialize child constraint
                    // ===================================================================================
                    // ===================================================================================

                    //
                    // Initialize child constraint. Allow children as much size as they want along the stack.
                    //
                    Size childConstraint = constraint;
                    if (isHorizontal)
                    {
                        childConstraint.Width = Double.PositiveInfinity;
                        if (IsScrolling && CanVerticallyScroll) { childConstraint.Height = Double.PositiveInfinity; }
                    }
                    else
                    {
                        childConstraint.Height = Double.PositiveInfinity;
                        if (IsScrolling && CanHorizontallyScroll) { childConstraint.Width = Double.PositiveInfinity; }
                    }

                    remeasure = false;
                    _actualItemsInExtendedViewportCount = 0;
                    _firstItemInExtendedViewportIndex = 0;
                    _firstItemInExtendedViewportOffset = 0.0;
                    _firstItemInExtendedViewportChildIndex = 0;

                    bool visualOrderChanged = false;
                    int childIndex = 0;
                    GeneratorPosition startPos;
                    bool hasBringIntoViewContainerBeenMeasured = false;

                    if (itemCount > 0)
                    {
                        // We will generate containers in several batches - one for
                        // the first visible item and the before-cache, another for
                        // the remaining visible items and the after-cache, and possibly
                        // more if the first attempts don't work out.   We want to keep
                        // the generator's status at "GeneratingContainers" throughout the
                        // entire process.  GenerateBatches does exactly what we want.
                        using (((ItemContainerGenerator)generator).GenerateBatches())
                        {
                            // ===================================================================================
                            // ===================================================================================
                            // Generate and measure children cached before the viewport.
                            // ===================================================================================
                            // ===================================================================================

                            if (!foundFirstItemInViewport ||
                                !IsEndOfCache(isHorizontal, cacheSize.CacheBeforeViewport, cacheUnit, stackPixelSizeInCacheBeforeViewport, stackLogicalSizeInCacheBeforeViewport) ||
                                !IsEndOfViewport(isHorizontal, viewport, stackPixelSizeInViewport))
                            {
                                bool adjustToChangeInFirstItem = false;

                                do
                                {
                                    Debug.Assert(!adjustToChangeInFirstItem || foundFirstItemInViewport, "This loop should only happen twice at most");

                                    adjustToChangeInFirstItem = false;

                                    //
                                    // Figure out the generator position
                                    //
                                    int startIndex;
                                    bool isAlwaysBeforeFirstItem = false;
                                    bool isAlwaysAfterFirstItem = false;
                                    bool isAlwaysAfterLastItem = false;
                                    if (IsViewportEmpty(isHorizontal, viewport) && DoubleUtil.GreaterThan(cacheSize.CacheBeforeViewport, 0.0))
                                    {
                                        isAlwaysBeforeFirstItem = true;
                                    }

                                    startIndex = firstItemInViewportIndex;
                                    startPos = IndexToGeneratorPositionForStart(firstItemInViewportIndex, out childIndex);
                                    firstItemInViewportChildIndex = childIndex;
                                    _firstItemInExtendedViewportIndex = firstItemInViewportIndex;
                                    _firstItemInExtendedViewportOffset = firstItemInViewportOffset;
                                    _firstItemInExtendedViewportChildIndex = childIndex;

                                    using (generator.StartAt(startPos, GeneratorDirection.Backward, true))
                                    {
                                        for (int i = startIndex; i >= 0; i--)
                                        {
                                            object item = items[i];

                                            MeasureChild(
                                                ref generator,
                                                ref itemStorageProvider,
                                                ref parentItem,
                                                ref hasUniformOrAverageContainerSizeBeenSet,
                                                ref computedUniformOrAverageContainerSize,
                                                ref computedAreContainersUniformlySized,
                                                ref items,
                                                ref item,
                                                ref children,
                                                ref _firstItemInExtendedViewportChildIndex,
                                                ref visualOrderChanged,
                                                ref isHorizontal,
                                                ref childConstraint,
                                                ref viewport,
                                                ref cacheSize,
                                                ref cacheUnit,
                                                ref foundFirstItemInViewport,
                                                ref firstItemInViewportOffset,
                                                ref stackPixelSize,
                                                ref stackPixelSizeInViewport,
                                                ref stackPixelSizeInCacheBeforeViewport,
                                                ref stackPixelSizeInCacheAfterViewport,
                                                ref stackLogicalSize,
                                                ref stackLogicalSizeInViewport,
                                                ref stackLogicalSizeInCacheBeforeViewport,
                                                ref stackLogicalSizeInCacheAfterViewport,
                                                ref mustDisableVirtualization,
                                                (i < firstItemInViewportIndex) || isAlwaysBeforeFirstItem,
                                                isAlwaysAfterFirstItem,
                                                isAlwaysAfterLastItem,
                                                false /*skipActualMeasure*/,
                                                false /*skipGeneration*/,
                                                ref hasBringIntoViewContainerBeenMeasured,
                                                ref hasVirtualizingChildren);

                                            if (ItemsChangedDuringMeasure)
                                            {
                                                // if the Items collection changed, our state is now invalid.  Start over.
                                                remeasure = true;
                                                goto EscapeMeasure;
                                            }

                                            _actualItemsInExtendedViewportCount++;

                                            if (!foundFirstItemInViewport)
                                            {
                                                //
                                                // Re-compute index and offset of first item in the viewport
                                                //
                                                SyncUniformSizeFlags(parentItem, children, items, itemStorageProvider, itemCount,
                                                    computedAreContainersUniformlySized, computedUniformOrAverageContainerSize, ref areContainersUniformlySized, ref uniformOrAverageContainerSize, isHorizontal, false /* evaluateAreContainersUniformlySized */);

                                                ComputeFirstItemInViewportIndexAndOffset(items, itemCount, itemStorageProvider, viewport, cacheSize,
                                                    isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize,
                                                    out firstItemInViewportOffset,
                                                    out firstItemInViewportIndex,
                                                    out foundFirstItemInViewport);

                                                if (foundFirstItemInViewport)
                                                {
                                                    if (i == firstItemInViewportIndex)
                                                    {
                                                        MeasureChild(
                                                            ref generator,
                                                            ref itemStorageProvider,
                                                            ref parentItem,
                                                            ref hasUniformOrAverageContainerSizeBeenSet,
                                                            ref computedUniformOrAverageContainerSize,
                                                            ref computedAreContainersUniformlySized,
                                                            ref items,
                                                            ref item,
                                                            ref children,
                                                            ref _firstItemInExtendedViewportChildIndex,
                                                            ref visualOrderChanged,
                                                            ref isHorizontal,
                                                            ref childConstraint,
                                                            ref viewport,
                                                            ref cacheSize,
                                                            ref cacheUnit,
                                                            ref foundFirstItemInViewport,
                                                            ref firstItemInViewportOffset,
                                                            ref stackPixelSize,
                                                            ref stackPixelSizeInViewport,
                                                            ref stackPixelSizeInCacheBeforeViewport,
                                                            ref stackPixelSizeInCacheAfterViewport,
                                                            ref stackLogicalSize,
                                                            ref stackLogicalSizeInViewport,
                                                            ref stackLogicalSizeInCacheBeforeViewport,
                                                            ref stackLogicalSizeInCacheAfterViewport,
                                                            ref mustDisableVirtualization,
                                                            false /*isBeforeFirstItem*/,
                                                            false /*isAfterFirstItem*/,
                                                            false /*isAfterLastItem*/,
                                                            true /*skipActualMeasure*/,
                                                            true /*skipGeneration*/,
                                                            ref hasBringIntoViewContainerBeenMeasured,
                                                            ref hasVirtualizingChildren);

                                                        if (ItemsChangedDuringMeasure)
                                                        {
                                                            // if the Items collection changed, our state is now invalid.  Start over.
                                                            remeasure = true;
                                                            goto EscapeMeasure;
                                                        }
                                                    }
                                                    else
                                                    {
                                                        stackPixelSize = new Size();
                                                        stackLogicalSize = new Size();
                                                        _actualItemsInExtendedViewportCount--;
                                                        adjustToChangeInFirstItem = true;
                                                        break;
                                                    }
                                                }
                                                else
                                                {
                                                    break;
                                                }
                                            }

                                            //
                                            // If this is the end of the cache before the viewport break out of the loop.
                                            //
                                            if (IsEndOfCache(isHorizontal, cacheSize.CacheBeforeViewport, cacheUnit, stackPixelSizeInCacheBeforeViewport, stackLogicalSizeInCacheBeforeViewport))
                                            {
                                                break;
                                            }

                                            _firstItemInExtendedViewportIndex = Math.Max(_firstItemInExtendedViewportIndex - 1, 0);
                                            IndexToGeneratorPositionForStart(_firstItemInExtendedViewportIndex, out _firstItemInExtendedViewportChildIndex);
                                            _firstItemInExtendedViewportChildIndex = Math.Max(_firstItemInExtendedViewportChildIndex, 0);
                                        }
                                    }
                                }
                                while (adjustToChangeInFirstItem);

                                ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, _firstItemInExtendedViewportIndex, out _firstItemInExtendedViewportOffset);
                            }

                            if (foundFirstItemInViewport &&
                                (!IsEndOfCache(isHorizontal, cacheSize.CacheAfterViewport, cacheUnit, stackPixelSizeInCacheAfterViewport, stackLogicalSizeInCacheAfterViewport) ||
                                 !IsEndOfViewport(isHorizontal, viewport, stackPixelSizeInViewport)))
                            {
                                //
                                // Figure out the generator position
                                //
                                int startIndex;
                                bool isAlwaysBeforeFirstItem = false;
                                bool isAlwaysAfterFirstItem = false;
                                bool isAlwaysAfterLastItem = false;

                                if (IsViewportEmpty(isHorizontal, viewport))
                                {
                                    startIndex = 0;
                                    isAlwaysAfterFirstItem = true;
                                    isAlwaysAfterLastItem = true;
                                }
                                else
                                {
                                    startIndex = firstItemInViewportIndex + 1;
                                    isAlwaysAfterFirstItem = true;
                                }

                                startPos = IndexToGeneratorPositionForStart(startIndex, out childIndex);

                                // ===================================================================================
                                // ===================================================================================
                                // Generate and measure children in the viewport and cached after the viewport.
                                // ===================================================================================
                                // ===================================================================================
                                using (generator.StartAt(startPos, GeneratorDirection.Forward, true))
                                {
                                    for (int i = startIndex; i < itemCount; i++, childIndex++)
                                    {
                                        object item = items[i];

                                        MeasureChild(
                                            ref generator,
                                            ref itemStorageProvider,
                                            ref parentItem,
                                            ref hasUniformOrAverageContainerSizeBeenSet,
                                            ref computedUniformOrAverageContainerSize,
                                            ref computedAreContainersUniformlySized,
                                            ref items,
                                            ref item,
                                            ref children,
                                            ref childIndex,
                                            ref visualOrderChanged,
                                            ref isHorizontal,
                                            ref childConstraint,
                                            ref viewport,
                                            ref cacheSize,
                                            ref cacheUnit,
                                            ref foundFirstItemInViewport,
                                            ref firstItemInViewportOffset,
                                            ref stackPixelSize,
                                            ref stackPixelSizeInViewport,
                                            ref stackPixelSizeInCacheBeforeViewport,
                                            ref stackPixelSizeInCacheAfterViewport,
                                            ref stackLogicalSize,
                                            ref stackLogicalSizeInViewport,
                                            ref stackLogicalSizeInCacheBeforeViewport,
                                            ref stackLogicalSizeInCacheAfterViewport,
                                            ref mustDisableVirtualization,
                                            isAlwaysBeforeFirstItem,
                                            (i > firstItemInViewportIndex) || isAlwaysAfterFirstItem,
                                            (i > lastItemInViewportIndex) || isAlwaysAfterLastItem,
                                            false /*skipActualMeasure*/,
                                            false /*skipGeneration*/,
                                            ref hasBringIntoViewContainerBeenMeasured,
                                            ref hasVirtualizingChildren);

                                            if (ItemsChangedDuringMeasure)
                                            {
                                                // if the Items collection changed, our state is now invalid.  Start over.
                                                remeasure = true;
                                                goto EscapeMeasure;
                                            }

                                        _actualItemsInExtendedViewportCount++;

                                        if (IsEndOfViewport(isHorizontal, viewport, stackPixelSizeInViewport))
                                        {
                                            //
                                            // If this is the last item in the original viewport make a record of it.
                                            //
                                            if (!foundLastItemInViewport)
                                            {
                                                foundLastItemInViewport = true;
                                                lastItemInViewportIndex = i;
                                            }

                                            //
                                            // If this is the end of the cache after the viewport break out of the loop.
                                            //
                                            if (IsEndOfCache(isHorizontal, cacheSize.CacheAfterViewport, cacheUnit, stackPixelSizeInCacheAfterViewport, stackLogicalSizeInCacheAfterViewport))
                                            {
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (IsVirtualizing &&
                        !IsPixelBased &&
                        (hasVirtualizingChildren || virtualizationInfoProvider != null) &&
                        (MeasureCaches || (DoubleUtil.AreClose(cacheSize.CacheBeforeViewport, 0) && DoubleUtil.AreClose(cacheSize.CacheAfterViewport, 0))))
                    {
                        //
                        // All of the descendent panels in hierarchical item scrolling scenarios that are after the extended
                        // viewport need to be measured so that they do not arrange any of their children above their own
                        // bounds and hence show through in the viewport.
                        //
                        int startIndex = _firstItemInExtendedViewportChildIndex+_actualItemsInExtendedViewportCount;
                        int childrenCount = children.Count;
                        for (int i=startIndex; i<childrenCount; i++)
                        {
                            MeasureExistingChildBeyondExtendedViewport(
                                ref generator,
                                ref itemStorageProvider,
                                ref parentItem,
                                ref hasUniformOrAverageContainerSizeBeenSet,
                                ref computedUniformOrAverageContainerSize,
                                ref computedAreContainersUniformlySized,
                                ref items,
                                ref children,
                                ref i,
                                ref visualOrderChanged,
                                ref isHorizontal,
                                ref childConstraint,
                                ref foundFirstItemInViewport,
                                ref firstItemInViewportOffset,
                                ref mustDisableVirtualization,
                                ref hasVirtualizingChildren,
                                ref hasBringIntoViewContainerBeenMeasured);

                                if (ItemsChangedDuringMeasure)
                                {
                                    // if the Items collection changed, our state is now invalid.  Start over.
                                    remeasure = true;
                                    goto EscapeMeasure;
                                }
                        }
                    }

                    if (_bringIntoViewContainer != null && !hasBringIntoViewContainerBeenMeasured)
                    {
                        //
                        // Measure the container meant to be brought into view in preparation for the next MakeVisible operation
                        //
                        childIndex = children.IndexOf(_bringIntoViewContainer);
                        if (childIndex < 0)
                        {
                            //
                            // If there were a collection changed between the BringIndexIntoView call
                            // and the current Measure then it is possible that the item for the
                            // _bringIntoViewContainer has been removed from the collection and so
                            // has the container. We need to guard against this scenario. See Dev11
                            // bug# 172296.
                            //
                            _bringIntoViewContainer = null;
                        }
                        else
                        {
                            MeasureExistingChildBeyondExtendedViewport(
                                ref generator,
                                ref itemStorageProvider,
                                ref parentItem,
                                ref hasUniformOrAverageContainerSizeBeenSet,
                                ref computedUniformOrAverageContainerSize,
                                ref computedAreContainersUniformlySized,
                                ref items,
                                ref children,
                                ref childIndex,
                                ref visualOrderChanged,
                                ref isHorizontal,
                                ref childConstraint,
                                ref foundFirstItemInViewport,
                                ref firstItemInViewportOffset,
                                ref mustDisableVirtualization,
                                ref hasVirtualizingChildren,
                                ref hasBringIntoViewContainerBeenMeasured);

                                if (ItemsChangedDuringMeasure)
                                {
                                    // if the Items collection changed, our state is now invalid.  Start over.
                                    remeasure = true;
                                    goto EscapeMeasure;
                                }
                        }
                    }

                    SyncUniformSizeFlags(parentItem, children, items, itemStorageProvider, itemCount,
                        computedAreContainersUniformlySized, computedUniformOrAverageContainerSize, ref areContainersUniformlySized, ref uniformOrAverageContainerSize, isHorizontal, false /* evaluateAreContainersUniformlySized */);

                    if (IsVirtualizing)
                    {
#if DEBUG
                        if (InRecyclingMode)
                        {
                            debug_VerifyRealizedChildren();
                        }
#endif

                        // ===================================================================================
                        // ===================================================================================
                        // Acount for the size of items before and after the viewport that won't be generated
                        // ===================================================================================
                        // ===================================================================================
                        ExtendPixelAndLogicalSizes(
                            children,
                            items,
                            itemCount,
                            itemStorageProvider,
                            areContainersUniformlySized,
                            uniformOrAverageContainerSize,
                            ref stackPixelSize,
                            ref stackLogicalSize,
                            isHorizontal,
                            _firstItemInExtendedViewportIndex,
                            _firstItemInExtendedViewportChildIndex,
                            true /*before */);

                        ExtendPixelAndLogicalSizes(
                            children,
                            items,
                            itemCount,
                            itemStorageProvider,
                            areContainersUniformlySized,
                            uniformOrAverageContainerSize,
                            ref stackPixelSize,
                            ref stackLogicalSize,
                            isHorizontal,
                            _firstItemInExtendedViewportIndex + _actualItemsInExtendedViewportCount,
                            _firstItemInExtendedViewportChildIndex + _actualItemsInExtendedViewportCount,
                            false /*before */);
                    }

                    if (IsScrolling)
                    {
                        // Adjust for position of panel within the viewport
                        EnsurePixelOffsetFromViewport();
                        if (isHorizontal)
                        {
                            if (!DoubleUtil.IsZero(stackPixelSizeInCacheBeforeViewport.Width))
                            {
                                stackPixelSizeInCacheBeforeViewport.Width += _pixelOffsetFromViewport.X;
                            }
                        }
                        else
                        {
                            if (!DoubleUtil.IsZero(stackPixelSizeInCacheBeforeViewport.Height))
                            {
                                stackPixelSizeInCacheBeforeViewport.Height += _pixelOffsetFromViewport.Y;
                            }
                        }
                    }

                    // ===================================================================================
                    // ===================================================================================
                    // [....] members that may be required during Arrange or in later Measure passes
                    // ===================================================================================
                    // ===================================================================================
                    _previousStackPixelSizeInViewport = stackPixelSizeInViewport;
                    _previousStackLogicalSizeInViewport = stackLogicalSizeInViewport;
                    _previousStackPixelSizeInCacheBeforeViewport = stackPixelSizeInCacheBeforeViewport;

                    // Coerce infinite viewport dimensions to stackPixelSize
                    if (double.IsInfinity(viewport.Width))
                    {
                        viewport.Width = stackPixelSize.Width;
                    }
                    if (double.IsInfinity(viewport.Height))
                    {
                        viewport.Height = stackPixelSize.Height;
                    }

                    _extendedViewport = ExtendViewport(
                                            virtualizationInfoProvider,
                                            isHorizontal,
                                            viewport,
                                            cacheSize,
                                            cacheUnit,
                                            stackPixelSizeInCacheBeforeViewport,
                                            stackLogicalSizeInCacheBeforeViewport,
                                            stackPixelSizeInCacheAfterViewport,
                                            stackLogicalSizeInCacheAfterViewport,
                                            stackPixelSize,
                                            stackLogicalSize,
                                            ref _itemsInExtendedViewportCount);

                    // It is important that this be set after the call to ExtendedViewport because that method uses the previous value of _viewport
                    _viewport = viewport;

                    // ===================================================================================
                    // ===================================================================================
                    // Store the sizes that have been computed on the parent
                    // ===================================================================================
                    // ===================================================================================
                    if (virtualizationInfoProvider != null && IsVisible)
                    {
                        //
                        // Note that it is possible to receive a Measure request even if the panel is
                        // actually not visible. This has been observed in a recycling TreeView where
                        // recycled TreeViewItems often switch IsExpanded states when representing
                        // different pieces of data. The IsVisible check above is to account for this scenario.
                        //
                        virtualizationInfoProvider.ItemDesiredSizes = new HierarchicalVirtualizationItemDesiredSizes(
                            stackLogicalSize,
                            stackLogicalSizeInViewport,
                            stackLogicalSizeInCacheBeforeViewport,
                            stackLogicalSizeInCacheAfterViewport,
                            stackPixelSize,
                            stackPixelSizeInViewport,
                            stackPixelSizeInCacheBeforeViewport,
                            stackPixelSizeInCacheAfterViewport);
                        virtualizationInfoProvider.MustDisableVirtualization = mustDisableVirtualization;
                    }

                    if (MustDisableVirtualization != mustDisableVirtualization)
                    {
                        MustDisableVirtualization = mustDisableVirtualization;
                        remeasure |= IsScrolling;
                    }

                    // ===================================================================================
                    // ===================================================================================
                    // Adjust the scroll offset, extent, etc.
                    // ===================================================================================
                    // ===================================================================================
                    if (IsScrolling)
                    {
                        SetAndVerifyScrollingData(
                            isHorizontal,
                            viewport,
                            constraint,
                            ref stackPixelSize,
                            ref stackLogicalSize,
                            ref stackPixelSizeInViewport,
                            ref stackLogicalSizeInViewport,
                            ref stackPixelSizeInCacheBeforeViewport,
                            ref stackLogicalSizeInCacheBeforeViewport,
                            ref remeasure,
                            ref lastPageSafeOffset,
                            ref previouslyMeasuredOffsets);
                    }

                    EscapeMeasure:
                    // ===================================================================================
                    // ===================================================================================
                    // Cleanup items no longer in the viewport
                    // ===================================================================================
                    // ===================================================================================
                    if (!remeasure)
                    {
                        if (IsVirtualizing)
                        {
                            if (InRecyclingMode)
                            {
                                DisconnectRecycledContainers();

                                if (visualOrderChanged)
                                {
                                    //
                                    // We moved some containers in the visual tree without firing
                                    // changed events.  ZOrder is now invalid.
                                    //
                                    InvalidateZState();
                                }
                            }
                            else
                            {
                                EnsureCleanupOperation(false /*delay*/);
                            }
                        }
                        HasVirtualizingChildren = hasVirtualizingChildren;

                        debug_AssertRealizedChildrenEqualVisualChildren();
                    }
                }
            }
            finally
            {
                if (etwTracingEnabled)
                {
                    EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientStringEnd, EventTrace.Keyword.KeywordGeneral, EventTrace.Level.Info, "VirtualizingStackPanel :MeasureOverride");
                }
            }

            if (remeasure)
            {
                //
                // Make another pass of MeasureOverride if remeasure is true.
                //
                return MeasureOverrideImpl(constraint, lastPageSafeOffset, ref previouslyMeasuredOffsets, remeasure);
            }
            else
            {
                return stackPixelSize;
            }
        }
Example #16
0
        }// *** End DEAD CODE ***

        private void GetSizesForChildWithInset(
            bool isHorizontal,
            bool isChildHorizontal,
            bool isBeforeFirstItem,
            bool isAfterLastItem,
            IHierarchicalVirtualizationAndScrollInfo virtualizingChild,
            Size childDesiredSize,
            Rect childViewport,
            VirtualizationCacheLength childCacheSize,
            VirtualizationCacheLengthUnit childCacheUnit,
            out Size childPixelSize,
            out Size childPixelSizeInViewport,
            out Size childPixelSizeInCacheBeforeViewport,
            out Size childPixelSizeInCacheAfterViewport,
            out Size childLogicalSize,
            out Size childLogicalSizeInViewport,
            out Size childLogicalSizeInCacheBeforeViewport,
            out Size childLogicalSizeInCacheAfterViewport)
        {
            // set childPixelSize to childDesiredSize directly.   Ideally this should
            // be the same as adding the contributions from the items panel and
            // the front and back insets, as indicated by commented-out lines below,
            // but this isn't true when the inset is merely an estimate (i.e.
            // before the child has been arranged) - contributions from margins,
            // border thickness, etc. between the items panel and the virtualizingChild
            // are not yet accounted for.  The childDesiredSize is more accurate,
            // so we use that.
            childPixelSize = childDesiredSize;
            childPixelSizeInViewport = new Size();
            childPixelSizeInCacheBeforeViewport = new Size();
            childPixelSizeInCacheAfterViewport = new Size();

            childLogicalSize = new Size();
            childLogicalSizeInViewport = new Size();
            childLogicalSizeInCacheBeforeViewport = new Size();
            childLogicalSizeInCacheAfterViewport = new Size();

            HierarchicalVirtualizationItemDesiredSizes itemDesiredSizes =
                (virtualizingChild != null) ? virtualizingChild.ItemDesiredSizes
                                            : new HierarchicalVirtualizationItemDesiredSizes();

            // the interesting case is when there is nested virtualization.  It's not
            // enough to test whether virtualizingChild is non-null;  the child
            // may not have done any virtualization (eg. because its ItemsHost
            // is not a VSP).  Instead, test whether its ItemsHost recorded a
            // nonzero extent.
            if ((!isHorizontal &&  (itemDesiredSizes.PixelSize.Height > 0 ||
                                    itemDesiredSizes.LogicalSize.Height > 0)) ||
                ( isHorizontal &&  (itemDesiredSizes.PixelSize.Width > 0 ||
                                    itemDesiredSizes.LogicalSize.Width > 0)))
            {
                // itemDesiredSizes gives the contribution from the nested panel.
              //StackSizes(isHorizontal, ref childPixelSize, itemDesiredSizes.PixelSize); (omitted - see comment at initialization)
                StackSizes(isHorizontal, ref childPixelSizeInCacheBeforeViewport, itemDesiredSizes.PixelSizeBeforeViewport);
                StackSizes(isHorizontal, ref childPixelSizeInViewport, itemDesiredSizes.PixelSizeInViewport);
                StackSizes(isHorizontal, ref childPixelSizeInCacheAfterViewport, itemDesiredSizes.PixelSizeAfterViewport);

                StackSizes(isHorizontal, ref childLogicalSize, itemDesiredSizes.LogicalSize);
                StackSizes(isHorizontal, ref childLogicalSizeInCacheBeforeViewport, itemDesiredSizes.LogicalSizeBeforeViewport);
                StackSizes(isHorizontal, ref childLogicalSizeInViewport, itemDesiredSizes.LogicalSizeInViewport);
                StackSizes(isHorizontal, ref childLogicalSizeInCacheAfterViewport, itemDesiredSizes.LogicalSizeAfterViewport);

                // Now add the contributions from the inset.  First decide whether
                // the child's item belongs to the front or back inset
                Thickness inset = GetItemsHostInsetForChild(virtualizingChild);
                bool isHeaderBeforeItems = IsHeaderBeforeItems(isHorizontal, virtualizingChild as FrameworkElement, ref inset);

                // Add contributions from the front inset.
                Size frontPixelSize = isHorizontal ? new Size(Math.Max(inset.Left,0), childDesiredSize.Height)
                                                   : new Size(childDesiredSize.Width, Math.Max(inset.Top, 0));
                Size frontLogicalSize = isHeaderBeforeItems ? new Size(1,1) : new Size(0,0);

              //StackSizes(isHorizontal, ref childPixelSize, frontPixelSize); (omitted - see comment at initialization)
                StackSizes(isHorizontal, ref childLogicalSize, frontLogicalSize);
                GetSizesForChildIntersectingTheViewport(isHorizontal, isChildHorizontal,
                    frontPixelSize, frontLogicalSize,
                    childViewport,
                    ref childPixelSizeInViewport, ref childLogicalSizeInViewport,
                    ref childPixelSizeInCacheBeforeViewport, ref childLogicalSizeInCacheBeforeViewport,
                    ref childPixelSizeInCacheAfterViewport, ref childLogicalSizeInCacheAfterViewport);

                // Add contributions from the back inset.
                Size backPixelSize = isHorizontal ? new Size(Math.Max(inset.Right,0), childDesiredSize.Height)
                                                   : new Size(childDesiredSize.Width, Math.Max(inset.Bottom,0));
                Size backLogicalSize = isHeaderBeforeItems ? new Size(0,0) : new Size(1,1);

              //StackSizes(isHorizontal, ref childPixelSize, backPixelSize); (omitted - see comment at initialization)
                StackSizes(isHorizontal, ref childLogicalSize, backLogicalSize);

                // before calling the helper, adjust the viewport to account for
                // the contributions we've just made
                Rect adjustedChildViewport = childViewport;
                if (isHorizontal)
                {
                    adjustedChildViewport.X -= IsPixelBased ? frontPixelSize.Width + itemDesiredSizes.PixelSize.Width
                                                            : frontLogicalSize.Width + itemDesiredSizes.LogicalSize.Width;
                    adjustedChildViewport.Width = Math.Max(0, adjustedChildViewport.Width - childPixelSizeInViewport.Width);
                }
                else
                {
                    adjustedChildViewport.Y -= IsPixelBased ? frontPixelSize.Height + itemDesiredSizes.PixelSize.Height
                                                            : frontLogicalSize.Height + itemDesiredSizes.LogicalSize.Height;
                    adjustedChildViewport.Height = Math.Max(0, adjustedChildViewport.Height - childPixelSizeInViewport.Height);
                }
                GetSizesForChildIntersectingTheViewport(isHorizontal, isChildHorizontal,
                    backPixelSize, backLogicalSize,
                    adjustedChildViewport,
                    ref childPixelSizeInViewport, ref childLogicalSizeInViewport,
                    ref childPixelSizeInCacheBeforeViewport, ref childLogicalSizeInCacheBeforeViewport,
                    ref childPixelSizeInCacheAfterViewport, ref childLogicalSizeInCacheAfterViewport);
            }
            else
            {
                // there was no nested virtualization.  The only contributions come
                // directly from the given child.

              //childPixelSize = childDesiredSize; (already done at initialization)
                childLogicalSize = new Size(1, 1);

                if (isBeforeFirstItem)
                {
                    childPixelSizeInCacheBeforeViewport = childDesiredSize;
                    childLogicalSizeInCacheBeforeViewport = new Size(DoubleUtil.GreaterThan(childPixelSizeInCacheBeforeViewport.Width, 0) ? 1 : 0,
                                                                     DoubleUtil.GreaterThan(childPixelSizeInCacheBeforeViewport.Height, 0) ? 1 : 0);
                }
                else if (isAfterLastItem)
                {
                    childPixelSizeInCacheAfterViewport = childDesiredSize;
                    childLogicalSizeInCacheAfterViewport = new Size(DoubleUtil.GreaterThan(childPixelSizeInCacheAfterViewport.Width, 0) ? 1 : 0,
                                                                    DoubleUtil.GreaterThan(childPixelSizeInCacheAfterViewport.Height, 0) ? 1 : 0);
                }
                else
                {
                    GetSizesForChildIntersectingTheViewport(
                        isHorizontal,
                        isChildHorizontal,
                        childPixelSize,
                        childLogicalSize,
                        childViewport,
                        ref childPixelSizeInViewport,
                        ref childLogicalSizeInViewport,
                        ref childPixelSizeInCacheBeforeViewport,
                        ref childLogicalSizeInCacheBeforeViewport,
                        ref childPixelSizeInCacheAfterViewport,
                        ref childLogicalSizeInCacheAfterViewport);
                }
            }
        }
Example #17
0
        /// <summary>
        /// Returns the index of the first item visible (even partially) in the viewport.
        /// </summary>
        private void ComputeFirstItemInViewportIndexAndOffset(
            IList items,
            int itemCount,
            IContainItemStorage itemStorageProvider,
            Rect viewport,
            VirtualizationCacheLength cacheSize,
            bool isHorizontal,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            out double firstItemInViewportOffset,
            out int firstItemInViewportIndex,
            out bool foundFirstItemInViewport)
        {
            firstItemInViewportOffset = 0.0;
            firstItemInViewportIndex = 0;
            foundFirstItemInViewport = false;

            if (IsViewportEmpty(isHorizontal, viewport))
            {
                if (DoubleUtil.GreaterThan(cacheSize.CacheBeforeViewport, 0.0))
                {
                    firstItemInViewportIndex = itemCount-1;
                    ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, itemCount-1, out firstItemInViewportOffset);
                    foundFirstItemInViewport = true;
                }
                else
                {
                    //
                    // If the cacheSizeAfterViewport is also empty then we are merely
                    // here scouting to get a better measurement of this item.
                    //
                    firstItemInViewportIndex = 0;
                    firstItemInViewportOffset = 0;
                    foundFirstItemInViewport = DoubleUtil.GreaterThan(cacheSize.CacheAfterViewport, 0.0);
                }
            }
            else
            {
                //
                // Compute the span of this panel above the viewport. Note that if
                // the panel is below the viewport then this span is 0.0.
                //
                double spanBeforeViewport = Math.Max(isHorizontal ? viewport.X : viewport.Y, 0.0);

                if (areContainersUniformlySized)
                {
                    //
                    // This is an optimization for the case that all the children are of
                    // uniform dimension along the stacking axis. In this case the index
                    // and offset for the first item in the viewport is computed in constant time.
                    //
                    double childSize = uniformOrAverageContainerSize;
                    if (DoubleUtil.GreaterThan(childSize, 0))
                    {
                        firstItemInViewportIndex = (int)Math.Floor(spanBeforeViewport / childSize);
                        firstItemInViewportOffset = firstItemInViewportIndex * childSize;
                    }

                    foundFirstItemInViewport = (firstItemInViewportIndex < itemCount);
                    if (!foundFirstItemInViewport)
                    {
                        firstItemInViewportOffset = 0.0;
                        firstItemInViewportIndex = 0;
                    }
                }
                else
                {
                    if (DoubleUtil.AreClose(spanBeforeViewport, 0))
                    {
                        foundFirstItemInViewport = true;
                        firstItemInViewportOffset = 0.0;
                        firstItemInViewportIndex = 0;
                    }
                    else
                    {
                        Size containerSize;
                        double totalSpan = 0.0;      // total height or width in the stacking direction
                        double containerSpan = 0.0;
                        bool isVSP45Compat = IsVSP45Compat;

                        for (int i = 0; i < itemCount; i++)
                        {
                            object item = items[i];
                            GetContainerSizeForItem(itemStorageProvider, item, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, out containerSize);
                            containerSpan = isHorizontal ? containerSize.Width : containerSize.Height;
                            totalSpan += containerSpan;

                            // rounding errors while accumulating totalSpan can
                            // cause this loop to terminate one iteration too early,
                            // which leads to an infinite loop in recycling mode (Dev11 807561).
                            // Use LayoutDoubleUtil here (as in other calculations
                            // related to the viewport);  it is more tolerant of rounding error.
                            bool endsAfterViewport =
                                isVSP45Compat ? DoubleUtil.GreaterThan(totalSpan, spanBeforeViewport)
                                              : LayoutDoubleUtil.LessThan(spanBeforeViewport, totalSpan);

                            if (endsAfterViewport)
                            {
                                //
                                // This is the first item that starts before the  viewport but ends after it.
                                // It is thus the the first item in the viewport.
                                //
                                firstItemInViewportIndex = i;
                                firstItemInViewportOffset = totalSpan - containerSpan;
                                break;
                            }
                        }

                        foundFirstItemInViewport =
                                isVSP45Compat ? DoubleUtil.GreaterThan(totalSpan, spanBeforeViewport)
                                              : LayoutDoubleUtil.LessThan(spanBeforeViewport, totalSpan);
                        if (!foundFirstItemInViewport)
                        {
                            firstItemInViewportOffset = 0.0;
                            firstItemInViewportIndex = 0;
                        }
                    }
                }
            }

            if (ScrollTracer.IsEnabled && ScrollTracer.IsTracing(this))
            {
                ScrollTracer.Trace(this, ScrollTraceOp.CFIVIO,
                    viewport, foundFirstItemInViewport, firstItemInViewportIndex, firstItemInViewportOffset);
            }
        }
Example #18
0
        /// <summary>
        ///     Sets the value for <see cref="CacheLengthProperty" />.
        /// </summary>
        /// <param name="element">The element on which to set the value.</param>
        /// <param name="value">VirtualCacheLength representing the dimensions of the cache before and after the
        /// viewport.</param>
        public static void SetCacheLength(DependencyObject element, VirtualizationCacheLength value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            element.SetValue(CacheLengthProperty, value);
        }
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------

        #region Internal Methods

        /// <summary>
        /// Creates a string from a VirtualizationCacheLength given the CultureInfo.
        /// </summary>
        /// <param name="cacheLength">VirtualizationCacheLength.</param>
        /// <param name="cultureInfo">Culture Info.</param>
        /// <returns>Newly created string instance.</returns>
        static internal string ToString(VirtualizationCacheLength cacheLength, CultureInfo cultureInfo)
        {
            char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);

            // Initial capacity [64] is an estimate based on a sum of:
            // 24 = 2x double (twelve digits is generous for the range of values likely)
            //  2 = 2x separator characters
            //
            StringBuilder sb = new StringBuilder(26);

            sb.Append(cacheLength.CacheBeforeViewport.ToString(cultureInfo));
            sb.Append(listSeparator);
            sb.Append(cacheLength.CacheAfterViewport.ToString(cultureInfo));
            return sb.ToString();
        }
Example #20
0
        /// <summary>
        /// Adjusts viewport to accomodate the either the Header or ItemsPanel.
        /// </summary>
        private void AdjustNonScrollingViewport(
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            ref Rect viewport,
            ref VirtualizationCacheLength cacheLength,
            ref VirtualizationCacheLengthUnit cacheUnit,
            bool forHeader)
        {
            Debug.Assert(virtualizationInfoProvider != null, "This method should only be invoked for a virtualizing owner");
            Debug.Assert(cacheUnit != VirtualizationCacheLengthUnit.Page, "Page after cache size is not expected here.");

            Rect parentViewport = viewport;
            double sizeAfterStartViewportEdge = 0;
            double sizeBeforeStartViewportEdge = 0;
            double sizeAfterEndViewportEdge = 0;
            double sizeBeforeEndViewportEdge = 0;
            double cacheBeforeSize = cacheLength.CacheBeforeViewport;
            double cacheAfterSize = cacheLength.CacheAfterViewport;

            HierarchicalVirtualizationHeaderDesiredSizes headerDesiredSizes = virtualizationInfoProvider.HeaderDesiredSizes;
            HierarchicalVirtualizationItemDesiredSizes itemDesiredSizes = virtualizationInfoProvider.ItemDesiredSizes;

            Size pixelSize = forHeader ? headerDesiredSizes.PixelSize : itemDesiredSizes.PixelSize;
            Size logicalSize = forHeader ? headerDesiredSizes.LogicalSize : itemDesiredSizes.LogicalSize;

            RelativeHeaderPosition headerPosition = RelativeHeaderPosition.Top; // virtualizationInfoProvider.RelativeHeaderPosition;

            if ((forHeader && headerPosition == RelativeHeaderPosition.Left) ||
                (!forHeader && headerPosition == RelativeHeaderPosition.Right))
            {
                //
                // Adjust the offset
                //

                viewport.X -= IsPixelBased ? pixelSize.Width : logicalSize.Width;

                if (DoubleUtil.GreaterThan(parentViewport.X, 0))
                {
                    //
                    // Viewport is after the start of this panel
                    //

                    if (IsPixelBased && DoubleUtil.GreaterThan(pixelSize.Width, parentViewport.X))
                    {
                        //
                        // Header straddles the start edge of the viewport
                        //

                        sizeAfterStartViewportEdge = pixelSize.Width - parentViewport.X;
                        sizeBeforeStartViewportEdge = pixelSize.Width - sizeAfterStartViewportEdge;

                        viewport.Width = Math.Max(viewport.Width - sizeAfterStartViewportEdge, 0);

                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheBeforeSize = Math.Max(cacheBeforeSize - sizeBeforeStartViewportEdge, 0);
                        }
                        else
                        {
                            cacheBeforeSize = Math.Max(cacheBeforeSize - Math.Floor(logicalSize.Width * sizeBeforeStartViewportEdge / pixelSize.Width), 0);
                        }
                    }
                    else
                    {
                        //
                        // Header is completely before the start edge of the viewport. We do not need to
                        // adjust the cacheBefore size in this case because the cacheBefore is populated
                        // bottom up and we cant really be certain that the header will infact lie within the
                        // cacheBefore region.
                        //
                    }
                }
                else
                {
                    //
                    // Viewport is at or before this panel
                    //

                    if (DoubleUtil.GreaterThan(parentViewport.Width, 0))
                    {
                        if (DoubleUtil.GreaterThanOrClose(parentViewport.Width, pixelSize.Width))
                        {
                            //
                            // Header is completely within the viewport
                            //

                            viewport.Width = Math.Max(0, parentViewport.Width - pixelSize.Width);
                        }
                        else
                        {
                            //
                            // Header straddles the end edge of the viewport
                            //

                            sizeBeforeEndViewportEdge = parentViewport.Width;
                            sizeAfterEndViewportEdge = pixelSize.Width - sizeBeforeEndViewportEdge;

                            viewport.Width = 0;

                            if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                            {
                                cacheAfterSize = Math.Max(cacheAfterSize - sizeAfterEndViewportEdge, 0);
                            }
                            else
                            {
                                cacheAfterSize = Math.Max(cacheAfterSize - Math.Floor(logicalSize.Width * sizeAfterEndViewportEdge / pixelSize.Width), 0);
                            }
                        }
                    }
                    else
                    {
                        //
                        // Header is completely after the end edge of the viewport
                        //

                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheAfterSize = Math.Max(cacheAfterSize - pixelSize.Width, 0);
                        }
                        else
                        {
                            cacheAfterSize = Math.Max(cacheAfterSize - logicalSize.Width, 0);
                        }
                    }
                }
            }
            else if ((forHeader && headerPosition == RelativeHeaderPosition.Top) ||
                    (!forHeader && headerPosition == RelativeHeaderPosition.Bottom))
            {
                //
                // Adjust the offset
                //

                viewport.Y -= IsPixelBased ? pixelSize.Height : logicalSize.Height;

                if (DoubleUtil.GreaterThan(parentViewport.Y, 0))
                {
                    //
                    // Viewport is after the start of this panel
                    //

                    if (IsPixelBased && DoubleUtil.GreaterThan(pixelSize.Height, parentViewport.Y))
                    {
                        //
                        // Header straddles the start edge of the viewport
                        //

                        sizeAfterStartViewportEdge = pixelSize.Height - parentViewport.Y;
                        sizeBeforeStartViewportEdge = pixelSize.Height - sizeAfterStartViewportEdge;

                        viewport.Height = Math.Max(viewport.Height - sizeAfterStartViewportEdge, 0);

                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheBeforeSize = Math.Max(cacheBeforeSize - sizeBeforeStartViewportEdge, 0);
                        }
                        else
                        {
                            cacheBeforeSize = Math.Max(cacheBeforeSize - Math.Floor(logicalSize.Height * sizeBeforeStartViewportEdge / pixelSize.Height), 0);
                        }
                    }
                    else
                    {
                        //
                        // Header is completely before the start edge of the viewport. We do not need to
                        // adjust the cacheBefore size in this case because the cacheBefore is populated
                        // bottom up and we cant really be certain that the header will infact lie within the
                        // cacheBefore region.
                        //
                    }
                }
                else
                {
                    //
                    // Viewport is at or before the start of this panel
                    //

                    if (DoubleUtil.GreaterThan(parentViewport.Height, 0))
                    {
                        if (DoubleUtil.GreaterThanOrClose(parentViewport.Height, pixelSize.Height))
                        {
                            //
                            // Header is completely within the viewport
                            //

                            viewport.Height = Math.Max(0, parentViewport.Height - pixelSize.Height);
                        }
                        else
                        {
                            //
                            // Header straddles the end edge of the viewport
                            //

                            sizeBeforeEndViewportEdge = parentViewport.Height;
                            sizeAfterEndViewportEdge = pixelSize.Height - sizeBeforeEndViewportEdge;

                            viewport.Height = 0;

                            if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                            {
                                cacheAfterSize = Math.Max(cacheAfterSize - sizeAfterEndViewportEdge, 0);
                            }
                            else
                            {
                                cacheAfterSize = Math.Max(cacheAfterSize - Math.Floor(logicalSize.Height * sizeAfterEndViewportEdge / pixelSize.Height), 0);
                            }
                        }
                    }
                    else
                    {
                        //
                        // Header is completely after the end edge of the viewport
                        //

                        if (cacheUnit == VirtualizationCacheLengthUnit.Pixel)
                        {
                            cacheAfterSize = Math.Max(cacheAfterSize - pixelSize.Height, 0);
                        }
                        else
                        {
                            cacheAfterSize = Math.Max(cacheAfterSize - logicalSize.Height, 0);
                        }
                    }
                }
            }

            cacheLength = new VirtualizationCacheLength(cacheBeforeSize, cacheAfterSize);
        }
Example #21
0
 /// <summary>
 /// Adjusts viewport to accomodate the items.
 /// </summary>
 private void AdjustNonScrollingViewportForItems(IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
                                                     ref Rect viewport,
                                                     ref VirtualizationCacheLength cacheLength,
                                                     ref VirtualizationCacheLengthUnit cacheLengthUnit)
 {
     bool forHeader = false;
     AdjustNonScrollingViewport(virtualizationInfoProvider, ref viewport, ref cacheLength, ref cacheLengthUnit, forHeader);
 }
Example #22
0
        /// <summary>
        /// Initializes the viewport for this panel.
        /// </summary>
        private void InitializeViewport(
            object parentItem,
            IContainItemStorage parentItemStorageProvider,
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            bool isHorizontal,
            Size constraint,
            ref Rect viewport,
            ref VirtualizationCacheLength cacheSize,
            ref VirtualizationCacheLengthUnit cacheUnit,
            out Rect extendedViewport)
        {

            Size extent = new Size();
            bool isVSP45Compat = IsVSP45Compat;

            if (IsScrolling)
            {
                //
                // We're the top level scrolling panel. Fetch the offset from the _scrollData.
                //

                Size size;
                double offsetX, offsetY;
                Size viewportSize;

                size = constraint;
                offsetX = _scrollData._offset.X;
                offsetY = _scrollData._offset.Y;
                extent = _scrollData._extent;
                viewportSize = _scrollData._viewport;

                if (!IsScrollActive || IgnoreMaxDesiredSize)
                {
                    _scrollData._maxDesiredSize = new Size();
                }

                if (IsPixelBased)
                {
                    viewport = new Rect(offsetX, offsetY, size.Width, size.Height);
                    CoerceScrollingViewportOffset(ref viewport, extent, isHorizontal);
                }
                else
                {
                    viewport = new Rect(offsetX, offsetY, viewportSize.Width, viewportSize.Height);
                    CoerceScrollingViewportOffset(ref viewport, extent, isHorizontal);
                    viewport.Size = size;
                }

                if (IsVirtualizing)
                {
                    cacheSize = VirtualizingStackPanel.GetCacheLength(this);
                    cacheUnit = VirtualizingStackPanel.GetCacheLengthUnit(this);

                    if (DoubleUtil.GreaterThan(cacheSize.CacheBeforeViewport, 0) ||
                        DoubleUtil.GreaterThan(cacheSize.CacheAfterViewport, 0))
                    {
                        if (!MeasureCaches)
                        {
                            WasLastMeasurePassAnchored = (_firstContainerInViewport != null) || (_bringIntoViewLeafContainer != null);

                            DispatcherOperation measureCachesOperation = MeasureCachesOperationField.GetValue(this);
                            if (measureCachesOperation == null)
                            {
                                Action measureCachesAction = null;
                                int retryCount = 3;
                                measureCachesAction = (Action)delegate()
                                    {
                                        Debug.Assert(retryCount >=0, "retry MeasureCaches too often");
                                        bool isLayoutDirty = (0 < retryCount--) && (MeasureDirty || ArrangeDirty);
                                        try
                                        {
                                            if (isVSP45Compat || !isLayoutDirty)
                                            {
                                                MeasureCachesOperationField.ClearValue(this);

                                                MeasureCaches = true;

                                                if (WasLastMeasurePassAnchored)
                                                {
                                                    SetAnchorInformation(isHorizontal);
                                                }

                                                InvalidateMeasure();
                                                UpdateLayout();
                                            }
                                        }
                                        finally
                                        {
                                            // check whether UpdateLayout finished the job
                                            isLayoutDirty = isLayoutDirty ||
                                                    ((0 < retryCount) && (MeasureDirty || ArrangeDirty));
                                            if (!isVSP45Compat && isLayoutDirty)
                                            {
                                                // try the measure-cache pass again later.
                                                // Note that we only do this when:
                                                // 1. this VSP's layout is dirty, either because
                                                //    a. it was dirty to begin with so we
                                                //       skipped UpdateLayout, or
                                                //    b. UpdateLayout ran, but left this VSP's
                                                //       layout dirty.
                                                // 2. we haven't run out of retries
                                                // 3. we're not in 4.5-compat mode
                                                //
                                                // (1) can happen if layout times out and moves to
                                                //     background.
                                                // (2) protects against loops when an app calls
                                                //     VSP.Measure directly, outside of the normal
                                                //     layout system (real appps don't do this,
                                                //     but test code does - it happens in the DrtXaml test).
                                                // (3) preserves compat with 4.5RTM, where the
                                                //     "move to background" situation led to an
                                                //     infinite loop.
                                                MeasureCachesOperationField.SetValue(this,
                                                    Dispatcher.BeginInvoke(DispatcherPriority.Background, measureCachesAction));
                                            }

                                            MeasureCaches = false;

                                            // If there is a pending anchor operation that got registered in
                                            // the current pass, or if layout didn't finish, we don't want to
                                            // clear the IsScrollActive flag.
                                            // We should allow that measure pass to also settle and then clear
                                            // the flag.

                                            DispatcherOperation anchoredInvalidateMeasureOperation = AnchoredInvalidateMeasureOperationField.GetValue(this);
                                            if (anchoredInvalidateMeasureOperation == null && (isVSP45Compat || !isLayoutDirty))
                                            {
                                                if (isVSP45Compat)
                                                {
                                                    IsScrollActive = false;
                                                }
                                                else if (IsScrollActive)
                                                {
                                                    // keep IsScrollActive set until the
                                                    // anchored measure has occurred.  It may
                                                    // need to remeasure, which should count
                                                    // as part of the scroll operation
                                                    DispatcherOperation clearIsScrollActiveOperation = ClearIsScrollActiveOperationField.GetValue(this);
                                                    if (clearIsScrollActiveOperation != null)
                                                    {
                                                        clearIsScrollActiveOperation.Abort();
                                                    }
                                                    clearIsScrollActiveOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                                        (Action)ClearIsScrollActive);

                                                    ClearIsScrollActiveOperationField.SetValue(this, clearIsScrollActiveOperation);
                                                }
                                            }
                                        }
                                    };
                                measureCachesOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                                                                measureCachesAction);
                                MeasureCachesOperationField.SetValue(this, measureCachesOperation);
                            }
                        }
                    }
                    else if (IsScrollActive)
                    {
                        DispatcherOperation clearIsScrollActiveOperation = ClearIsScrollActiveOperationField.GetValue(this);
                        if (clearIsScrollActiveOperation == null)
                        {
                            clearIsScrollActiveOperation = Dispatcher.BeginInvoke(DispatcherPriority.Background,
                                (Action)ClearIsScrollActive);

                            ClearIsScrollActiveOperationField.SetValue(this, clearIsScrollActiveOperation);
                        }
                    }

                    NormalizeCacheLength(isHorizontal, viewport, ref cacheSize, ref cacheUnit);
                }
                else
                {
                    cacheSize = new VirtualizationCacheLength(
                        Double.PositiveInfinity,
                        IsViewportEmpty(isHorizontal, viewport) ?
                        0.0 :
                        Double.PositiveInfinity);
                    cacheUnit = VirtualizationCacheLengthUnit.Pixel;

                    ClearAsyncOperations();
                }
            }
            else if (virtualizationInfoProvider != null)
            {
                //
                // Adjust the viewport offset for a non scrolling panel to account for the HeaderSize
                // when virtualizing.
                //
                HierarchicalVirtualizationConstraints virtualizationConstraints = virtualizationInfoProvider.Constraints;
                viewport = virtualizationConstraints.Viewport;
                cacheSize = virtualizationConstraints.CacheLength;
                cacheUnit = virtualizationConstraints.CacheLengthUnit;
                MeasureCaches = virtualizationInfoProvider.InBackgroundLayout;

                if (isVSP45Compat)
                {
                    AdjustNonScrollingViewportForHeader(virtualizationInfoProvider, ref viewport, ref cacheSize, ref cacheUnit);
                }
                else
                {
                    AdjustNonScrollingViewportForInset(isHorizontal, parentItem, parentItemStorageProvider, virtualizationInfoProvider, ref viewport, ref cacheSize, ref cacheUnit);

                    // The viewport position may be expressed in an old coordinate system
                    // relying on an old average container size.  Using that position would
                    // produce bad results;  for example the first step in Measure computes
                    // the first item that intersects the viewport - it uses the latest
                    // average container size, and hence would choose the wrong item
                    // (from user's point of view, the panel scrolls to a random place).
                    //      To work around this, ComputeEffectiveOffset stores a list
                    // of substitute offsets when the ave container size changes;
                    // this instructs this method to replace an old offset with a new
                    // one (appearing last) that's the equivalent in the current coordinate
                    // system.
                    //      This replacement stays in effect until the parent panel gives us
                    // an offset from a more recent coordinate change, after which older
                    // offsets won't appear again.   Or an offset that's not on the
                    // list at all, which means a new scroll motion has started.
                    DependencyObject container = virtualizationInfoProvider as DependencyObject;
                    List<Double> offsetList = EffectiveOffsetInformationField.GetValue(container);
                    if (offsetList != null)
                    {
                        if (ScrollTracer.IsEnabled && ScrollTracer.IsTracing(this))
                        {
                            object[] args = new object[offsetList.Count + 1];
                            args[0] = viewport.Location;
                            for (int i=0; i<offsetList.Count; ++i)
                            {
                                args[i+1] = offsetList[i];
                            }
                            ScrollTracer.Trace(this, ScrollTraceOp.UseSubstOffset,
                                args);
                        }

                        // find the given offset on the list
                        double offset = isHorizontal ? viewport.X : viewport.Y;
                        int index = offsetList.IndexOf(offset);

                        // if it appears, susbstitue the last offset
                        if (index >= 0)
                        {
                            if (isHorizontal)
                            {
                                viewport.X = offsetList[offsetList.Count-1];
                            }
                            else
                            {
                                viewport.Y = offsetList[offsetList.Count-1];
                            }

                            // and remove offsets before the matching one -
                            // they'll never be needed again
                            offsetList.RemoveRange(0, index);
                        }

                        // if the list is no longer needed, discard it
                        if (index < 0 || offsetList.Count <= 1)
                        {
                            EffectiveOffsetInformationField.ClearValue(container);
                        }
                    }
                }
            }
            else
            {
                viewport = new Rect(0, 0, constraint.Width, constraint.Height);

                if (isHorizontal)
                {
                    viewport.Width = Double.PositiveInfinity;
                }
                else
                {
                    viewport.Height = Double.PositiveInfinity;
                }
            }

            // Adjust extendedViewport

            extendedViewport = _extendedViewport;

            if (isHorizontal)
            {
                extendedViewport.X += viewport.X - _viewport.X;
            }
            else
            {
                extendedViewport.Y += viewport.Y - _viewport.Y;
            }
        }