Example #1
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;
        }
Example #2
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;
            }
        }
Example #3
0
        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;
        }
Example #4
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");
        }
Example #5
0
        // returns true if the cached average size changed
        private bool 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.");

            bool result = false;

            //
            // 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.
                    if (UniformOrAverageContainerSize != value)
                    {
                        UniformOrAverageContainerSize = value;
                        result = true;
                    }
                }
                else
                {
                    object oldValue = itemStorageProvider.ReadItemValue(item, UniformOrAverageContainerSizeProperty);
                    itemStorageProvider.StoreItemValue(item, UniformOrAverageContainerSizeProperty, value);
                    result = !Object.Equals(oldValue, value);
                }
            }

            return result;
        }
Example #6
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);
            }
        }
Example #7
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();
                    }
                }
            }
        }
Example #8
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;
        }