Beispiel #1
0
        // Token: 0x060064D7 RID: 25815 RVA: 0x001C4A4C File Offset: 0x001C2C4C
        internal static void StoreItemValues(IContainItemStorage owner, DependencyObject container, object item)
        {
            int[]            itemValueStorageIndices = Helper.ItemValueStorageIndices;
            DependencyObject owner2 = (DependencyObject)owner;

            foreach (int num in itemValueStorageIndices)
            {
                EntryIndex entryIndex = container.LookupEntry(num);
                if (entryIndex.Found)
                {
                    EffectiveValueEntry effectiveValueEntry = container.EffectiveValues[(int)entryIndex.Index];
                    if ((effectiveValueEntry.BaseValueSourceInternal == BaseValueSourceInternal.Local || effectiveValueEntry.BaseValueSourceInternal == BaseValueSourceInternal.ParentTemplate) && !effectiveValueEntry.HasModifiers)
                    {
                        Helper.StoreItemValue(owner2, item, num, effectiveValueEntry.Value);
                    }
                    else if (effectiveValueEntry.IsCoercedWithCurrentValue)
                    {
                        Helper.StoreItemValue(owner2, item, num, new Helper.ModifiedItemValue(effectiveValueEntry.ModifiedValue.CoercedValue, FullValueSource.IsCoercedWithCurrentValue));
                    }
                    else
                    {
                        Helper.ClearItemValue(owner2, item, num);
                    }
                }
            }
        }
        /// <summary>
        /// This method is called upon to compute the pixel and logical
        /// distances for the itemCount beginning at the the start index.
        /// </summary>
        private void ComputeDistance(
            IList items,
            IContainItemStorage itemStorageProvider,
            bool isHorizontal,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            int startIndex,
            int itemCount,
            out double distance)
        {
            distance = 0.0;

            if (areContainersUniformlySized)
            {
                //
                // Performance optimization for the most general case where
                // all the children are of uniform size along the stacking direction.
                // Note that the computation of the range size is performed in constant time.
                //
                double childSize = uniformOrAverageContainerSize;
                if (isHorizontal)
                {
                    distance += childSize * itemCount;
                }
                else
                {
                    distance += childSize * itemCount;
                }
            }
            else
            {
                for (int i = startIndex; i < startIndex + itemCount; i++)
                {
                    object item = items[i];

                    Size containerSize;
                    GetContainerSizeForItem(itemStorageProvider, item, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize,
                        out containerSize);

                    if (isHorizontal)
                    {
                        distance += containerSize.Width;
                    }
                    else
                    {
                        distance += containerSize.Height;
                    }
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Initializes the owner and interfaces for the virtualization services it supports.
        /// </summary>
        private void GetOwners(
            bool shouldSetVirtualizationState,
            bool isHorizontal,
            out ItemsControl itemsControl,
            out GroupItem groupItem,
            out IContainItemStorage itemStorageProvider,
            out IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            out object parentItem,
            out IContainItemStorage parentItemStorageProvider,
            out bool mustDisableVirtualization)
        {
            groupItem = null;
            parentItem = null;
            parentItemStorageProvider = null;

            bool isScrolling = IsScrolling;

            mustDisableVirtualization = isScrolling ? MustDisableVirtualization : false;

            ItemsControl parentItemsControl = null;
            DependencyObject itemsOwner = ItemsControl.GetItemsOwnerInternal(this, out itemsControl);
            if (itemsOwner != itemsControl)
            {
                groupItem = itemsOwner as GroupItem;
                parentItem = itemsControl.ItemContainerGenerator.ItemFromContainer(groupItem);
            }
            else if (!isScrolling)
            {
                parentItemsControl = ItemsControl.GetItemsOwnerInternal(VisualTreeHelper.GetParent(itemsControl)) as ItemsControl;
                if (parentItemsControl != null)
                {
                    parentItem = parentItemsControl.ItemContainerGenerator.ItemFromContainer(itemsControl);
                }
                else
                {
                    parentItem = this;
                }
            }
            else
            {
                parentItem = this;
            }

            itemStorageProvider = itemsOwner as IContainItemStorage;
            virtualizationInfoProvider = null;
            parentItemStorageProvider = (IsVSP45Compat || isScrolling || itemsOwner == null) ? null :
                ItemsControl.GetItemsOwnerInternal(VisualTreeHelper.GetParent(itemsOwner)) as IContainItemStorage;

            if (groupItem != null)
            {
                virtualizationInfoProvider = GetVirtualizingProvider(groupItem);
                mustDisableVirtualization = virtualizationInfoProvider != null ? virtualizationInfoProvider.MustDisableVirtualization : false;
            }
            else if (!isScrolling)
            {
                virtualizationInfoProvider = GetVirtualizingProvider(itemsControl);
                mustDisableVirtualization = virtualizationInfoProvider != null ? virtualizationInfoProvider.MustDisableVirtualization : false;
            }

            if (shouldSetVirtualizationState)
            {
                // this is a good opportunity to set up tracing, if requested
                if (ScrollTracer.IsEnabled && !ScrollTracer.IsConfigured(this))
                {
                    ScrollTracer.ConfigureTracing(this, itemsOwner, parentItem, itemsControl);
                }

                //
                // Synchronize properties such as IsVirtualizing, IsRecycling & IsPixelBased
                //
                SetVirtualizationState(itemStorageProvider, itemsControl, mustDisableVirtualization);
            }
        }
        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;
            }
        }
        private void SetUniformOrAverageContainerSize(IContainItemStorage itemStorageProvider, object item, double value)
        {
            Debug.Assert(itemStorageProvider != null || item == this, "An item storage provider must be available.");
            Debug.Assert(item != null, "An item must be available.");

            //
            // This case was detected when entering a ListBoxItem into the ListBox through the XAML editor
            // in VS. In this case the ListBoxItem is empty and is of zero size. We do not want to record the
            // size of that ListBoxItem as the uniformOrAverageSize because on the next measure this will
            // lead to a divide by zero error when computing the firstItemInViewportIndex.
            //
            if (DoubleUtil.GreaterThan(value, 0))
            {
                if (item == this)
                {
                    // Set the cache if for VSP.
                    UniformOrAverageContainerSize = value;
                }
                else
                {
                    itemStorageProvider.StoreItemValue(item, UniformOrAverageContainerSizeProperty, value);
                }
            }
        }
 private double GetUniformOrAverageContainerSize(IContainItemStorage itemStorageProvider, object item)
 {
     bool hasUniformOrAverageContainerSizeBeenSet;
     return GetUniformOrAverageContainerSize(itemStorageProvider, item, out hasUniformOrAverageContainerSizeBeenSet);
 }
        private bool GetAreContainersUniformlySized(IContainItemStorage itemStorageProvider, object item)
        {
            Debug.Assert(itemStorageProvider != null || item == this, "An item storage provider must be available.");
            Debug.Assert(item != null, "An item must be available.");

            if (item == this)
            {
                if (AreContainersUniformlySized.HasValue)
                {
                    // Return the cached value if for VSP and if present.
                    return (bool)AreContainersUniformlySized;
                }
            }
            else
            {
                object value = itemStorageProvider.ReadItemValue(item, AreContainersUniformlySizedProperty);
                if (value != null)
                {
                    return (bool)value;
                }
            }

            return true;
        }
        /// <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);
            }
        }
Beispiel #9
0
        private void SyncUniformSizeFlags(
            object parentItem,
            IContainItemStorage parentItemStorageProvider,
            IList children,
            IList items,
            IContainItemStorage itemStorageProvider,
            int itemCount,
            bool computedAreContainersUniformlySized,
            double computedUniformOrAverageContainerSize,
            ref bool areContainersUniformlySized,
            ref double uniformOrAverageContainerSize,
            ref bool hasAverageContainerSizeChanged,
            bool isHorizontal,
            bool evaluateAreContainersUniformlySized)
        {
            bool isVSP45Compat = IsVSP45Compat;

            // 4.5 used the wrong ItemStorageProvider for the AreUniformlySized flag
            if (isVSP45Compat)
            {
                parentItemStorageProvider = itemStorageProvider;
            }

            if (evaluateAreContainersUniformlySized || areContainersUniformlySized != computedAreContainersUniformlySized)
            {
                Debug.Assert(evaluateAreContainersUniformlySized || !computedAreContainersUniformlySized, "AreContainersUniformlySized starts off true and can only be flipped to false.");

                if (!evaluateAreContainersUniformlySized)
                {
                    areContainersUniformlySized = computedAreContainersUniformlySized;
                    SetAreContainersUniformlySized(parentItemStorageProvider, parentItem, areContainersUniformlySized);
                }

                for (int i=0; i < children.Count; i++)
                {
                    UIElement child = children[i] as UIElement;
                    if (child != null && VirtualizingPanel.GetShouldCacheContainerSize(child))
                    {
                        IHierarchicalVirtualizationAndScrollInfo virtualizingChild  = GetVirtualizingChild(child);

                        Size childSize;

                        if (virtualizingChild != null)
                        {
                            if (isVSP45Compat)
                            {
                                HierarchicalVirtualizationHeaderDesiredSizes headerDesiredSizes = virtualizingChild.HeaderDesiredSizes;
                                HierarchicalVirtualizationItemDesiredSizes itemDesiredSizes = virtualizingChild.ItemDesiredSizes;

                                if (IsPixelBased)
                                {
                                    childSize = new Size(Math.Max(headerDesiredSizes.PixelSize.Width, itemDesiredSizes.PixelSize.Width),
                                                                  headerDesiredSizes.PixelSize.Height + itemDesiredSizes.PixelSize.Height);
                                }
                                else
                                {
                                    childSize = new Size(Math.Max(headerDesiredSizes.LogicalSize.Width, itemDesiredSizes.LogicalSize.Width),
                                                                  headerDesiredSizes.LogicalSize.Height + itemDesiredSizes.LogicalSize.Height);
                                }
                            }
                            else
                            {
                                HierarchicalVirtualizationItemDesiredSizes itemDesiredSizes = virtualizingChild.ItemDesiredSizes;

                                if (IsPixelBased)
                                {
                                    object v = child.ReadLocalValue(ItemsHostInsetProperty);
                                    if (v != DependencyProperty.UnsetValue)
                                    {
                                        // inset has been set - add it to the ItemsHost size
                                        Thickness inset = (Thickness)v;
                                        childSize = new Size(inset.Left + itemDesiredSizes.PixelSize.Width + inset.Right,
                                                             inset.Top + itemDesiredSizes.PixelSize.Height + inset.Bottom);
                                    }
                                    else
                                    {
                                        // inset has not been set (typically because
                                        // child doesn't have an ItemsHost).  Use
                                        // the child's desired size
                                        childSize = child.DesiredSize;
                                    }
                                }
                                else
                                {
                                    childSize = isHorizontal ? new Size(1 + itemDesiredSizes.LogicalSize.Width,
                                                                        Math.Max(1, itemDesiredSizes.LogicalSize.Height))
                                                             : new Size(Math.Max(1, itemDesiredSizes.LogicalSize.Width),
                                                                        1 + itemDesiredSizes.LogicalSize.Height);
                                }
                            }
                        }
                        else
                        {
                            if (IsPixelBased)
                            {
                                childSize = child.DesiredSize;
                            }
                            else
                            {
                                childSize = new Size(DoubleUtil.GreaterThan(child.DesiredSize.Width, 0) ? 1 : 0,
                                                     DoubleUtil.GreaterThan(child.DesiredSize.Height, 0) ? 1 : 0);
                            }
                        }

                        if (evaluateAreContainersUniformlySized && computedAreContainersUniformlySized)
                        {
                            if (isHorizontal)
                            {
                                computedAreContainersUniformlySized = DoubleUtil.AreClose(childSize.Width, uniformOrAverageContainerSize);
                            }
                            else
                            {
                                computedAreContainersUniformlySized = DoubleUtil.AreClose(childSize.Height, uniformOrAverageContainerSize);
                            }

                            if (!computedAreContainersUniformlySized)
                            {
                                // We need to restart the loop and cache
                                // the sizes of all children prior to this one

                                i = -1;
                            }
                        }
                        else
                        {
                            itemStorageProvider.StoreItemValue(((ItemContainerGenerator)Generator).ItemFromContainer(child), ContainerSizeProperty, childSize);
                        }
                    }
                }

                if (evaluateAreContainersUniformlySized)
                {
                    areContainersUniformlySized = computedAreContainersUniformlySized;
                    SetAreContainersUniformlySized(parentItemStorageProvider, parentItem, areContainersUniformlySized);
                }
            }

            if (!computedAreContainersUniformlySized)
            {
                Size containerSize;
                double sumOfContainerSizes = 0;
                int numContainerSizes = 0;

                for (int i=0; i<itemCount; i++)
                {
                    object value = itemStorageProvider.ReadItemValue(items[i], ContainerSizeProperty);
                    if (value != null)
                    {
                        containerSize = (Size)value;

                        if (isHorizontal)
                        {
                            sumOfContainerSizes += containerSize.Width;
                            numContainerSizes++;
                        }
                        else
                        {
                            sumOfContainerSizes += containerSize.Height;
                            numContainerSizes++;
                        }
                    }
                }

                if (numContainerSizes > 0)
                {
                    if (IsPixelBased)
                    {
                        uniformOrAverageContainerSize = sumOfContainerSizes / numContainerSizes;
                    }
                    else
                    {
                        uniformOrAverageContainerSize = Math.Round(sumOfContainerSizes / numContainerSizes);
                    }

                    if (SetUniformOrAverageContainerSize(parentItemStorageProvider, parentItem, uniformOrAverageContainerSize)
                        && !IsVSP45Compat)
                    {
                        hasAverageContainerSizeChanged = true;
                    }
                }
            }
            else
            {
                uniformOrAverageContainerSize = computedUniformOrAverageContainerSize;
            }

            if (ScrollTracer.IsEnabled && ScrollTracer.IsTracing(this))
            {
                ScrollTracer.Trace(this, ScrollTraceOp.SyncAveSize,
                    uniformOrAverageContainerSize, areContainersUniformlySized, hasAverageContainerSizeChanged);
            }
        }
Beispiel #10
0
        private void SetItemsHostInsetForChild(int index, UIElement child, IContainItemStorage itemStorageProvider, bool isHorizontal)
        {
            Debug.Assert(!IsVSP45Compat, "SetItemsHostInset should not be called in VSP45-compat mode");

            // this only applies to a hierarchical element with a visible ItemsHost
            bool isChildHorizontal = isHorizontal;
            IHierarchicalVirtualizationAndScrollInfo virtualizingChild = GetVirtualizingChild(child, ref isChildHorizontal);
            Panel itemsHost = (virtualizingChild == null) ? null : virtualizingChild.ItemsHost;
            if (itemsHost == null || !itemsHost.IsVisible)
                return;

            // get the transformation from child coords to itemsHost coords
            GeneralTransform transform = child.TransformToDescendant(itemsHost);
            if (transform == null)
                return;     // when transform is undefined, ItemsHost is effectively invisible

            // build a rect (in child coords) describing the child's extended frame
            FrameworkElement fe = virtualizingChild as FrameworkElement;
            Thickness margin = (fe == null) ? new Thickness() : fe.Margin;
            Rect childRect = new Rect(new Point(), child.DesiredSize);
            childRect.Offset(-margin.Left, -margin.Top);

            // transform to itemsHost coords
            Rect itemsRect = transform.TransformBounds(childRect);

            // compute the desired inset, avoiding catastrophic cancellation errors
            Size itemsSize = itemsHost.DesiredSize;
            double left = DoubleUtil.AreClose(0, itemsRect.Left) ? 0 : -itemsRect.Left;
            double top = DoubleUtil.AreClose(0, itemsRect.Top) ? 0 : -itemsRect.Top;
            double right = DoubleUtil.AreClose(itemsSize.Width, itemsRect.Right) ? 0 : itemsRect.Right-itemsSize.Width;
            double bottom = DoubleUtil.AreClose(itemsSize.Height, itemsRect.Bottom) ? 0 : itemsRect.Bottom-itemsSize.Height;
            Thickness inset = new Thickness(left, top, right, bottom);


            // get the item to use as the key into items storage
            object item = GetItemFromContainer(child);
            if (item == DependencyProperty.UnsetValue)
            {
                Debug.Assert(false, "SetInset should only be called for a container");
                return;
            }

            // see whether inset is changing
            object box = itemStorageProvider.ReadItemValue(item, ItemsHostInsetProperty);
            bool changed = (box == null);
            bool remeasure = changed;
            if (!changed)
            {
                Thickness oldInset = (Thickness)box;
                changed = !(    DoubleUtil.AreClose(oldInset.Left, inset.Left) &&
                                DoubleUtil.AreClose(oldInset.Top, inset.Top ) &&
                                DoubleUtil.AreClose(oldInset.Right, inset.Right) &&
                                DoubleUtil.AreClose(oldInset.Bottom, inset.Bottom) );

                // only changes along the scrolling axis require a remeasure.
                // use a less stringent "AreClose" test;  experiments show that the
                // trailing inset can change due to roundoff error by an amount
                // that is larger than the tolerance in DoubleUtil, but not large
                // enough to warrant an expensive remeasure.
                remeasure = changed &&
                    (   (isHorizontal  && !(AreInsetsClose(oldInset.Left, inset.Left) &&
                                            AreInsetsClose(oldInset.Right, inset.Right)))
                     || (!isHorizontal && !(AreInsetsClose(oldInset.Top, inset.Top) &&
                                            AreInsetsClose(oldInset.Bottom, inset.Bottom))) );
            }

            if (changed)
            {
                // store the new inset
                itemStorageProvider.StoreItemValue(item, ItemsHostInsetProperty, inset);
                child.SetValue(ItemsHostInsetProperty, inset);
            }

            if (remeasure)
            {
                // re-measure the scrolling panel
                ItemsControl scrollingItemsControl = GetScrollingItemsControl(child);
                Panel scrollingPanel = (scrollingItemsControl == null) ? null : scrollingItemsControl.ItemsHost;
                if (scrollingPanel != null)
                {
                    VirtualizingStackPanel vsp = scrollingPanel as VirtualizingStackPanel;
                    if (vsp != null)
                    {
                        vsp.AnchoredInvalidateMeasure();
                    }
                    else
                    {
                        scrollingPanel.InvalidateMeasure();
                    }
                }
            }
        }
Beispiel #11
0
        private Thickness GetItemsHostInsetForChild(IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider, IContainItemStorage parentItemStorageProvider=null, object parentItem=null)
        {
            // This method is called in two ways:
            // 1) Before this panel has been measured.
            //      Args:  parentItemStorageProvider is non-null.
            //      This is called from AdjustNonScrollingViewportForInset, to
            //      get the viewport into the panel's coordinates.  We don't yet
            //      know the real inset, but the parentItemStorageProvider may
            //      have a last-known estimate.
            // 2) After this panel has been measured.
            //      Args: parentItemStorageProvider is null.
            //      This is called while measuring or arranging an ancestor panel,
            //      who needs to know the inset for this panel.  In this case,
            //      the inset is already stored on the container, either by an
            //      earlier query of type 1, or during this panel's arrange.

            FrameworkElement container = virtualizationInfoProvider as FrameworkElement;
            Debug.Assert(parentItemStorageProvider != null || container != null,
                "Caller of GetItemsHostInsetForChild must provide either an ItemsStorageProvider or a container");

            // type 2 - get the value directly from the container
            if (parentItemStorageProvider == null)
            {
                return (Thickness)container.GetValue(ItemsHostInsetProperty);
            }

            // type 1 - get the last-known inset
            Thickness inset = new Thickness();
            object box = parentItemStorageProvider.ReadItemValue(parentItem, ItemsHostInsetProperty);
            if (box != null)
            {
                inset = (Thickness)box;
            }
            else if ((box = container.ReadLocalValue(ItemsHostInsetProperty)) != DependencyProperty.UnsetValue)
            {
                // recycled container - use the recycled value
                inset = (Thickness)box;
            }
            else
            {
                // first-time, use header desired size as a guess.  This is correct
                // for the default container templates.   Even better guess - include
                // the container's margin.
                HierarchicalVirtualizationHeaderDesiredSizes headerDesiredSizes = virtualizationInfoProvider.HeaderDesiredSizes;
                Thickness margin = container.Margin;
                inset.Top = headerDesiredSizes.PixelSize.Height + margin.Top;
                inset.Left = headerDesiredSizes.PixelSize.Width + margin.Left;

                // store the value, for use by later queries of type 1
                parentItemStorageProvider.StoreItemValue(parentItem, ItemsHostInsetProperty, inset);
            }

            // store the value, for use by later queries of type 2
            container.SetValue(ItemsHostInsetProperty, inset);

            return inset;
        }
Beispiel #12
0
        /// <summary>
        /// After a measure pass, compute the effective offset - taking into account
        /// changes to the average container size discovered during measure.  This
        /// is the inverse of the previous method - ComputeFirstItemInViewportIndexAndOffset -
        /// in the sense that it produces an offset that, if fed back into that method (with
        /// the revised average container sizes), will yield the same result obtained
        /// in the current measure pass.  That is, the same item will be selected
        /// as the first item, and likewise for its sub-items.
        /// </summary>
        private double ComputeEffectiveOffset(
            ref Rect viewport,
            DependencyObject firstContainer,
            int itemIndex,
            double firstItemOffset,
            IList items,
            IContainItemStorage itemStorageProvider,
            IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            bool isHorizontal,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize)
        {
            if (firstContainer == null)
            {
                return -1.0;    // undefined if no children in view
            }

            Debug.Assert(itemIndex < items.Count, "index out of range");
            double oldOffset = isHorizontal ? viewport.X : viewport.Y;
            double newOffset;

            // start with the effective offset of the first item
            ComputeDistance(items, itemStorageProvider, isHorizontal,
                areContainersUniformlySized, uniformOrAverageContainerSize,
                0, itemIndex, out newOffset);

            // add the offset within the first item
            newOffset += (oldOffset - firstItemOffset);

            // if the item's container has recorded a substitute offset,
            // adjust newOffset by the same amount.   This has the effect of
            // giving the child panel the offset it wants the next time this
            // panel measures the child.
            List<Double> childOffsetList = EffectiveOffsetInformationField.GetValue(firstContainer);
            if (childOffsetList != null)
            {
                int count = childOffsetList.Count;
                newOffset += (childOffsetList[count-1] - childOffsetList[0]);
            }

            // record the answer (when not a top-level panel), for use by InitializeViewport.
            DependencyObject container = virtualizationInfoProvider as DependencyObject;
            if (container != null && oldOffset != newOffset)
            {
                // preserve the existing old offsets, if any, in case there are
                // multiple calls to measure this panel before the parent
                // adjusts to the change in our coordinate system, or calls from
                // a parent who set its own offset using an older offset from here
                List<Double> offsetList = EffectiveOffsetInformationField.GetValue(container);
                if (offsetList == null)
                {
                    offsetList = new List<Double>(2);
                    offsetList.Add(oldOffset);
                }

                offsetList.Add(newOffset);

                if (ScrollTracer.IsEnabled && ScrollTracer.IsTracing(this))
                {
                    object[] args = new object[offsetList.Count];
                    for (int i=0; i<offsetList.Count; ++i)
                    {
                        args[i] = offsetList[i];
                    }
                    ScrollTracer.Trace(this, ScrollTraceOp.StoreSubstOffset,
                        args);
                }

                EffectiveOffsetInformationField.SetValue(container, offsetList);
            }

            return newOffset;
        }
Beispiel #13
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);
            }
        }
Beispiel #14
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);
        }
Beispiel #15
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;
            }
        }
Beispiel #16
0
        /// <summary>
        /// Returns the size of the container for a given item.  The size can come from the container or a lookup in the ItemStorage
        /// </summary>
        private void GetContainerSizeForItem(
            IContainItemStorage itemStorageProvider,
            object item,
            bool isHorizontal,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            out Size containerSize)
        {
            containerSize = Size.Empty;

            if (areContainersUniformlySized)
            {
                //
                // This is a performance optimization for the case that the containers are unformly sized.
                //
                containerSize = new Size();
                double uniformSize = uniformOrAverageContainerSize;

                if (isHorizontal)
                {
                    containerSize.Width = uniformSize;
                    containerSize.Height = IsPixelBased ? DesiredSize.Height : 1;
                }
                else
                {
                    containerSize.Height = uniformSize;
                    containerSize.Width = IsPixelBased ? DesiredSize.Width : 1;
                }
            }
            else
            {
                //
                // We fetch the size of a container from the ItemStorage.
                // The size is cached if this item were previously realized.
                //
                object value = itemStorageProvider.ReadItemValue(item, ContainerSizeProperty);
                if (value != null)
                {
                    containerSize = (Size)value;
                }
                else
                {
                    //
                    // This item has never been realized previously. So use the average size.
                    //
                    containerSize = new Size();
                    double averageSize = uniformOrAverageContainerSize;

                    if (isHorizontal)
                    {
                        containerSize.Width = averageSize;
                        containerSize.Height = IsPixelBased ? DesiredSize.Height : 1;
                    }
                    else
                    {
                        containerSize.Height = averageSize;
                        containerSize.Width = IsPixelBased ? DesiredSize.Width : 1;
                    }
                }
            }

            Debug.Assert(!containerSize.IsEmpty, "We can't estimate an empty size");
        }
Beispiel #17
0
        /// <summary>
        /// Sets the size of the container for a given item. If the items aren't uniformly sized store it in the ItemStorage.
        /// </summary>
        private void SetContainerSizeForItem(
            IContainItemStorage itemStorageProvider,
            object parentItem,
            object item,
            Size containerSize,
            bool isHorizontal,
            ref bool hasUniformOrAverageContainerSizeBeenSet,
            ref double uniformOrAverageContainerSize,
            ref bool areContainersUniformlySized)
        {
            if (!hasUniformOrAverageContainerSizeBeenSet)
            {
                hasUniformOrAverageContainerSizeBeenSet = true;
                uniformOrAverageContainerSize = isHorizontal ? containerSize.Width : containerSize.Height;
                SetUniformOrAverageContainerSize(itemStorageProvider, parentItem, uniformOrAverageContainerSize);
            }
            else if (areContainersUniformlySized)
            {
                //
                // if we come across a child whose DesiredSize is different from _uniformOrAverageContainerSize
                // Once AreContainersUniformlySized becomes false, dont set it back ever.
                //
                if (isHorizontal)
                {
                    areContainersUniformlySized = DoubleUtil.AreClose(containerSize.Width, uniformOrAverageContainerSize);
                }
                else
                {
                    areContainersUniformlySized = DoubleUtil.AreClose(containerSize.Height, uniformOrAverageContainerSize);
                }
            }

            //
            // Save off the child's desired size for later. The stored size is useful in hierarchical virtualization
            // scenarios (Eg. TreeView, Grouping) to compute the index of the first visible item in the viewport
            // and to Arrange children in their proper locations.
            //
            if (!areContainersUniformlySized)
            {
                itemStorageProvider.StoreItemValue(item, ContainerSizeProperty, containerSize);
            }
        }
Beispiel #18
0
        /// <summary>
        /// Initializes the owner and interfaces for the virtualization services it supports.
        /// </summary>
        private void GetOwners(
            bool shouldSetVirtualizationState,
            bool isHorizontal,
            out ItemsControl itemsControl,
            out GroupItem groupItem,
            out IContainItemStorage itemStorageProvider,
            out IHierarchicalVirtualizationAndScrollInfo virtualizationInfoProvider,
            out object parentItem,
            out bool mustDisableVirtualization)
        {
            groupItem = null;
            parentItem = null;

            bool isScrolling = IsScrolling;

            mustDisableVirtualization = isScrolling ? MustDisableVirtualization : false;

            DependencyObject itemsOwner = ItemsControl.GetItemsOwnerInternal(this, out itemsControl);
            if (itemsOwner != itemsControl)
            {
                groupItem = itemsOwner as GroupItem;
                parentItem = itemsControl.ItemContainerGenerator.ItemFromContainer(groupItem);
            }
            else if (!isScrolling)
            {
                ItemsControl parentItemsControl = ItemsControl.GetItemsOwnerInternal(VisualTreeHelper.GetParent(itemsControl)) as ItemsControl;
                if (parentItemsControl != null)
                {
                    parentItem = parentItemsControl.ItemContainerGenerator.ItemFromContainer(itemsControl);
                }
                else
                {
                    parentItem = this;
                }
            }
            else
            {
                parentItem = this;
            }

            itemStorageProvider = itemsOwner as IContainItemStorage;
            virtualizationInfoProvider = null;

            if (groupItem != null)
            {
                virtualizationInfoProvider = GetVirtualizingProvider(groupItem);
                mustDisableVirtualization = virtualizationInfoProvider != null ? virtualizationInfoProvider.MustDisableVirtualization : false;
            }
            else if (!isScrolling)
            {
                virtualizationInfoProvider = GetVirtualizingProvider(itemsControl);
                mustDisableVirtualization = virtualizationInfoProvider != null ? virtualizationInfoProvider.MustDisableVirtualization : false;
            }

            if (shouldSetVirtualizationState)
            {
                //
                // Synchronize properties such as IsVirtualizing, IsRecycling & IsPixelBased
                //
                SetVirtualizationState(itemStorageProvider, itemsControl, mustDisableVirtualization);
            }
        }
Beispiel #19
0
        private void SyncUniformSizeFlags(
            object parentItem,
            IList children,
            IList items,
            IContainItemStorage itemStorageProvider,
            int itemCount,
            bool computedAreContainersUniformlySized,
            double computedUniformOrAverageContainerSize,
            ref bool areContainersUniformlySized,
            ref double uniformOrAverageContainerSize,
            bool isHorizontal,
            bool evaluateAreContainersUniformlySized)
        {
            if (evaluateAreContainersUniformlySized || areContainersUniformlySized != computedAreContainersUniformlySized)
            {
                Debug.Assert(evaluateAreContainersUniformlySized || !computedAreContainersUniformlySized, "AreContainersUniformlySized starts off true and can only be flipped to false.");

                if (!evaluateAreContainersUniformlySized)
                {
                    areContainersUniformlySized = computedAreContainersUniformlySized;
                    SetAreContainersUniformlySized(itemStorageProvider, parentItem, areContainersUniformlySized);
                }

                for (int i=0; i < children.Count; i++)
                {
                    UIElement child = children[i] as UIElement;
                    if (child != null && VirtualizingPanel.GetShouldCacheContainerSize(child))
                    {
                        IHierarchicalVirtualizationAndScrollInfo virtualizingChild  = GetVirtualizingChild(child);

                        Size childSize;

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

                            if (IsPixelBased)
                            {
                                childSize = new Size(Math.Max(headerDesiredSizes.PixelSize.Width, itemDesiredSizes.PixelSize.Width),
                                                              headerDesiredSizes.PixelSize.Height + itemDesiredSizes.PixelSize.Height);
                            }
                            else
                            {
                                childSize = new Size(Math.Max(headerDesiredSizes.LogicalSize.Width, itemDesiredSizes.LogicalSize.Width),
                                                              headerDesiredSizes.LogicalSize.Height + itemDesiredSizes.LogicalSize.Height);
                            }
                        }
                        else
                        {
                            if (IsPixelBased)
                            {
                                childSize = child.DesiredSize;
                            }
                            else
                            {
                                childSize = new Size(DoubleUtil.GreaterThan(child.DesiredSize.Width, 0) ? 1 : 0,
                                                     DoubleUtil.GreaterThan(child.DesiredSize.Height, 0) ? 1 : 0);
                            }
                        }

                        if (evaluateAreContainersUniformlySized && computedAreContainersUniformlySized)
                        {
                            if (isHorizontal)
                            {
                                computedAreContainersUniformlySized = DoubleUtil.AreClose(childSize.Width, uniformOrAverageContainerSize);
                            }
                            else
                            {
                                computedAreContainersUniformlySized = DoubleUtil.AreClose(childSize.Height, uniformOrAverageContainerSize);
                            }

                            if (!computedAreContainersUniformlySized)
                            {
                                // We need to restart the loop and cache
                                // the sizes of all children prior to this one

                                i = -1;
                            }
                        }
                        else
                        {
                            itemStorageProvider.StoreItemValue(((ItemContainerGenerator)Generator).ItemFromContainer(child), ContainerSizeProperty, childSize);
                        }
                    }
                }

                if (evaluateAreContainersUniformlySized)
                {
                    areContainersUniformlySized = computedAreContainersUniformlySized;
                    SetAreContainersUniformlySized(itemStorageProvider, parentItem, areContainersUniformlySized);
                }
            }

            if (!computedAreContainersUniformlySized)
            {
                Size containerSize;
                double sumOfContainerSizes = 0;
                int numContainerSizes = 0;

                for (int i=0; i<itemCount; i++)
                {
                    object value = itemStorageProvider.ReadItemValue(items[i], ContainerSizeProperty);
                    if (value != null)
                    {
                        containerSize = (Size)value;

                        if (isHorizontal)
                        {
                            sumOfContainerSizes += containerSize.Width;
                            numContainerSizes++;
                        }
                        else
                        {
                            sumOfContainerSizes += containerSize.Height;
                            numContainerSizes++;
                        }
                    }
                }

                if (DoubleUtil.GreaterThan(numContainerSizes, 0))
                {
                    if (IsPixelBased)
                    {
                        uniformOrAverageContainerSize = sumOfContainerSizes / numContainerSizes;
                    }
                    else
                    {
                        uniformOrAverageContainerSize = Math.Round(sumOfContainerSizes / numContainerSizes);
                    }

                    SetUniformOrAverageContainerSize(itemStorageProvider, parentItem, uniformOrAverageContainerSize);
                }
            }
            else
            {
                uniformOrAverageContainerSize = computedUniformOrAverageContainerSize;
            }
        }
Beispiel #20
0
        /// <summary>
        /// Sets up IsVirtualizing, VirtualizationMode, and IsPixelBased
        /// </summary>
        private void SetVirtualizationState(
            IContainItemStorage itemStorageProvider,
            ItemsControl itemsControl,
            bool mustDisableVirtualization)
        {
            if (itemsControl != null)
            {
                bool isVirtualizing = GetIsVirtualizing(itemsControl);
                bool isVirtualizingWhenGrouping = GetIsVirtualizingWhenGrouping(itemsControl);
                VirtualizationMode virtualizationMode = GetVirtualizationMode(itemsControl);
                bool isGrouping = itemsControl.IsGrouping;
                IsVirtualizing = !mustDisableVirtualization && ((!isGrouping && isVirtualizing) || (isGrouping && isVirtualizing && isVirtualizingWhenGrouping));

                ScrollUnit scrollUnit = GetScrollUnit(itemsControl);
                bool oldIsPixelBased = IsPixelBased;
                IsPixelBased = mustDisableVirtualization || (scrollUnit == ScrollUnit.Pixel);
                if (IsScrolling)
                {
                    if (!HasMeasured || oldIsPixelBased != IsPixelBased)
                    {
                        ClearItemValueStorageRecursive(itemStorageProvider, this);
                    }

                    SetCacheLength(this, GetCacheLength(itemsControl));
                    SetCacheLengthUnit(this, GetCacheLengthUnit(itemsControl));
                }

                //
                // Set up info on first measure
                //
                if (HasMeasured)
                {
                    VirtualizationMode oldVirtualizationMode = InRecyclingMode ? VirtualizationMode.Recycling : VirtualizationMode.Standard;
                    if (oldVirtualizationMode != virtualizationMode)
                    {
                        throw new InvalidOperationException(SR.Get(SRID.CantSwitchVirtualizationModePostMeasure));
                    }
                }
                else
                {
                    HasMeasured = true;
                }

                InRecyclingMode = (virtualizationMode == VirtualizationMode.Recycling);
            }
        }
Beispiel #21
0
        private void SetAreContainersUniformlySized(IContainItemStorage itemStorageProvider, object item, bool value)
        {
            Debug.Assert(itemStorageProvider != null || item == this, "An item storage provider must be available.");
            Debug.Assert(item != null, "An item must be available.");

            if (item == this)
            {
                // Set the cache if for VSP.
                AreContainersUniformlySized = value;
            }
            else
            {
                itemStorageProvider.StoreItemValue(item, AreContainersUniformlySizedProperty, value);
            }
        }
Beispiel #22
0
        private static void ClearItemValueStorageRecursive(IContainItemStorage itemStorageProvider, Panel itemsHost)
        {
            Helper.ClearItemValueStorage((DependencyObject)itemStorageProvider, _indicesStoredInItemValueStorage);

            UIElementCollection children = itemsHost.InternalChildren;
            int childrenCount = children.Count;
            for (int i=0; i<childrenCount; i++)
            {
                IHierarchicalVirtualizationAndScrollInfo virtualizingChild = children[i] as IHierarchicalVirtualizationAndScrollInfo;
                if (virtualizingChild != null)
                {
                    Panel childItemsHost = virtualizingChild.ItemsHost;
                    if (childItemsHost != null)
                    {
                        IContainItemStorage childItemStorageProvider = GetItemStorageProvider(childItemsHost);
                        if (childItemStorageProvider != null)
                        {
                            ClearItemValueStorageRecursive(childItemStorageProvider, childItemsHost);
                        }
                    }
                }
            }
        }
Beispiel #23
0
        private double GetUniformOrAverageContainerSize(IContainItemStorage itemStorageProvider, object item, out bool hasUniformOrAverageContainerSizeBeenSet)
        {
            Debug.Assert(itemStorageProvider != null || item == this, "An item storage provider must be available.");
            Debug.Assert(item != null, "An item must be available.");

            if (item == this)
            {
                if (UniformOrAverageContainerSize.HasValue)
                {
                    // Return the cached value if for VSP and if present.
                    hasUniformOrAverageContainerSizeBeenSet = true;
                    return (double)UniformOrAverageContainerSize;
                }
            }
            else
            {
                object value = itemStorageProvider.ReadItemValue(item, UniformOrAverageContainerSizeProperty);
                if (value != null)
                {
                    hasUniformOrAverageContainerSizeBeenSet = true;
                    return (double)value;
                }
            }

            hasUniformOrAverageContainerSizeBeenSet = false;
            return IsPixelBased ? ScrollViewer._scrollLineDelta : 1.0;
        }
Beispiel #24
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;
                        }
                    }
                }
            }
        }
Beispiel #25
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);
        }
Beispiel #26
0
        /// <summary>
        /// DesiredSize is normally computed by summing up the size of all items we've generated.  Pixel-based virtualization uses a 'full' desired size.
        /// This extends the given desired size beyond the visible items.  It will extend it by the items before or after the set of generated items.
        /// The given pivotIndex is the index of either the first or last item generated.
        /// </summary>
        private void ExtendPixelAndLogicalSizes(
            IList children,
            IList items,
            int itemCount,
            IContainItemStorage itemStorageProvider,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            ref Size stackPixelSize,
            ref Size stackLogicalSize,
            bool isHorizontal,
            int pivotIndex,
            int pivotChildIndex,
            bool before)
        {
            Debug.Assert(IsVirtualizing, "We should only need to extend the viewport beyond the generated items when virtualizing");

            //
            // If we're virtualizing the sum of all generated containers is not the true desired size since not all containers were generated.
            // In the old items-based mode it didn't matter because only the scrolling panel could virtualize and scrollviewer doesn't *really*
            // care about desired size.
            //
            // In pixel-based mode we need to compute the same desired size as if we weren't virtualizing.
            //

            double distance;
            if (before)
            {
                ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, pivotIndex, out distance);
            }
            else
            {
                ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, pivotIndex, itemCount - pivotIndex, out distance);
            }

            if (IsPixelBased)
            {
                if (isHorizontal)
                {
                    stackPixelSize.Width += distance;
                }
                else
                {
                    stackPixelSize.Height += distance;
                }
            }
            else
            {
                if (isHorizontal)
                {
                    stackLogicalSize.Width += distance;
                }
                else
                {
                    stackLogicalSize.Height += distance;
                }

                //
                // If there are containers beyond the extended
                // viewport then their sizes need to be added to
                // the stackPixelSize. This is only required in the
                // hierarchical cases to be able to arrange containers
                // beyond the extended viewport accurately.
                //

                if (!IsScrolling)
                {
                    int startIndex, count;

                    if (before)
                    {
                        startIndex = 0;
                        count = pivotChildIndex;
                    }
                    else
                    {
                        startIndex = pivotChildIndex;
                        count = children.Count;
                    }

                    for (int i=startIndex; i<count; i++)
                    {
                        Size childDesiredSize = ((UIElement)children[i]).DesiredSize;

                        if (isHorizontal)
                        {
                            stackPixelSize.Width += childDesiredSize.Width;
                        }
                        else
                        {
                            stackPixelSize.Height += childDesiredSize.Height;
                        }
                    }
                }
            }
        }
Beispiel #27
0
        private void ArrangeItemsBeyondTheExtendedViewport(
            bool isHorizontal,
            UIElement child,
            Size childDesiredSize,
            double arrangeLength,
            IList items,
            IItemContainerGenerator generator,
            IContainItemStorage itemStorageProvider,
            bool areContainersUniformlySized,
            double uniformOrAverageContainerSize,
            bool beforeExtendedViewport,
            ref Rect rcChild,
            ref Size previousChildSize,
            ref Point previousChildOffset,
            ref int previousChildItemIndex)
        {
            //
            // These are the items beyond the viewport. (Eg. Recyclable containers that
            // are waiting until next measure to be cleaned up, Elements that are kept
            // alive because they hold focus.) These element are arranged beyond the
            // visible viewport but at their right position. This is important because these
            // containers need to be brought into view when using keyboard navigation
            // and their arrange rect is what informs the scroillviewer of the right offset
            // to scroll to.
            //
            if (isHorizontal)
            {
                rcChild.Width = childDesiredSize.Width;
                rcChild.Height = Math.Max(arrangeLength, childDesiredSize.Height);

                if (IsPixelBased)
                {
                    int currChildItemIndex = ((ItemContainerGenerator)generator).IndexFromContainer(child, true /*returnLocalIndex*/);
                    double distance;

                    if (beforeExtendedViewport)
                    {
                        if (previousChildItemIndex == -1)
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, currChildItemIndex, out distance);
                        }
                        else
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, currChildItemIndex, previousChildItemIndex-currChildItemIndex, out distance);
                        }

                        rcChild.X = previousChildOffset.X - distance;
                        rcChild.Y = previousChildOffset.Y;
                    }
                    else
                    {
                        if (previousChildItemIndex == -1)
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, currChildItemIndex, out distance);
                        }
                        else
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, previousChildItemIndex, currChildItemIndex-previousChildItemIndex, out distance);
                        }

                        rcChild.X = previousChildOffset.X + distance;
                        rcChild.Y = previousChildOffset.Y;
                    }

                    previousChildItemIndex = currChildItemIndex;
                }
                else
                {
                    if (beforeExtendedViewport)
                    {
                        rcChild.X -= childDesiredSize.Width;
                    }
                    else
                    {
                        rcChild.X += previousChildSize.Width;
                    }
                }
            }
            else
            {
                rcChild.Height = childDesiredSize.Height;
                rcChild.Width = Math.Max(arrangeLength, childDesiredSize.Width);

                if (IsPixelBased)
                {
                    int currChildItemIndex = ((ItemContainerGenerator)generator).IndexFromContainer(child, true /*returnLocalIndex*/);
                    double distance;

                    if (beforeExtendedViewport)
                    {
                        if (previousChildItemIndex == -1)
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, currChildItemIndex, out distance);
                        }
                        else
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, currChildItemIndex, previousChildItemIndex-currChildItemIndex, out distance);
                        }

                        rcChild.Y = previousChildOffset.Y - distance;
                        rcChild.X = previousChildOffset.X;
                    }
                    else
                    {
                        if (previousChildItemIndex == -1)
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, 0, currChildItemIndex, out distance);
                        }
                        else
                        {
                            ComputeDistance(items, itemStorageProvider, isHorizontal, areContainersUniformlySized, uniformOrAverageContainerSize, previousChildItemIndex, currChildItemIndex-previousChildItemIndex, out distance);
                        }

                        rcChild.Y = previousChildOffset.Y + distance;
                        rcChild.X = previousChildOffset.X;
                    }

                    previousChildItemIndex = currChildItemIndex;
                }
                else
                {
                    if (beforeExtendedViewport)
                    {
                        rcChild.Y -= childDesiredSize.Height;
                    }
                    else
                    {
                        rcChild.Y += previousChildSize.Height;
                    }
                }
            }

            previousChildSize = childDesiredSize;
            previousChildOffset = rcChild.Location;

            child.Arrange(rcChild);
        }
        /// <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;
            }
        }