Example #1
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);
            }
        }
Example #2
0
        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);
                }
            }
        }
Example #3
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 #4
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);
            }
        }
Example #5
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 #6
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 #7
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;
        }