예제 #1
0
        public ILayoutable GetAt(int realizedIndex)
        {
            ILayoutable element;

            if (IsVirtualizingContext)
            {
                if (_realizedElements[realizedIndex] == null)
                {
                    // Sentinel. Create the element now since we need it.
                    int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
                    Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex);
                    element = _context.GetOrCreateElementAt(
                        dataIndex,
                        ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                    _realizedElements[realizedIndex] = element;
                }
                else
                {
                    element = _realizedElements[realizedIndex];
                }
            }
            else
            {
                // realizedIndex and dataIndex are the same (everything is realized)
                element = _context.GetOrCreateElementAt(
                    realizedIndex,
                    ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
            }

            return(element);
        }
예제 #2
0
        public ILayoutable GetAt(int realizedIndex)
        {
            ILayoutable element;

            if (IsVirtualizingContext)
            {
                if (_realizedElements[realizedIndex] == null)
                {
                    // Sentinel. Create the element now since we need it.
                    int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
                    element = _context.GetOrCreateElementAt(
                        dataIndex,
                        ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                    _realizedElements[realizedIndex] = element;
                }
                else
                {
                    element = _realizedElements[realizedIndex];
                }
            }
            else
            {
                // realizedIndex and dataIndex are the same (everything is realized)
                element = _context.GetOrCreateElementAt(
                    realizedIndex,
                    ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
            }

            return(element);
        }
        internal void EnsureElementSize(
            Size availableSize,
            VirtualizingLayoutContext context,
            double layoutItemWidth,
            double layoutItemHeight,
            UniformGridLayoutItemsStretch stretch,
            Orientation orientation,
            double minRowSpacing,
            double minColumnSpacing,
            int maxItemsPerLine)
        {
            if (maxItemsPerLine == 0)
            {
                maxItemsPerLine = 1;
            }

            if (context.ItemCount > 0)
            {
                // If the first element is realized we don't need to cache it or to get it from the context
                var realizedElement = FlowAlgorithm.GetElementIfRealized(0);
                if (realizedElement != null)
                {
                    realizedElement.Measure(availableSize);
                    SetSize(realizedElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);
                    _cachedFirstElement = null;
                }
                else
                {
                    if (_cachedFirstElement == null)
                    {
                        // we only cache if we aren't realizing it
                        _cachedFirstElement = context.GetOrCreateElementAt(
                            0,
                            ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); // expensive
                    }

                    _cachedFirstElement.Measure(availableSize);

                    SetSize(_cachedFirstElement, layoutItemWidth, layoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine);

                    // See if we can move ownership to the flow algorithm. If we can, we do not need a local cache.
                    bool added = FlowAlgorithm.TryAddElement0(_cachedFirstElement);
                    if (added)
                    {
                        _cachedFirstElement = null;
                    }
                }
            }
        }
예제 #4
0
        internal void EnsureElementSize(
            Size availableSize,
            VirtualizingLayoutContext context,
            double layoutItemWidth,
            double LayoutItemHeight,
            UniformGridLayoutItemsStretch stretch,
            Orientation orientation,
            double minRowSpacing,
            double minColumnSpacing)
        {
            if (context.ItemCount > 0)
            {
                // If the first element is realized we don't need to cache it or to get it from the context
                var realizedElement = FlowAlgorithm.GetElementIfRealized(0);
                if (realizedElement != null)
                {
                    realizedElement.Measure(availableSize);
                    SetSize(realizedElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing);
                    _cachedFirstElement = null;
                }
                else
                {
                    if (_cachedFirstElement == null)
                    {
                        // we only cache if we aren't realizing it
                        _cachedFirstElement = context.GetOrCreateElementAt(
                            0,
                            ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); // expensive
                    }

                    _cachedFirstElement.Measure(availableSize);

                    // This doesn't need to be done in the UWP version and I'm not sure why. If we
                    // don't do this here, and we receive a recycled element then it will be shown
                    // at its previous arrange point, but we don't want it shown at all until its
                    // arranged.
                    _cachedFirstElement.Arrange(new Rect(-10000.0, -10000.0, 0, 0));

                    SetSize(_cachedFirstElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing);

                    // See if we can move ownership to the flow algorithm. If we can, we do not need a local cache.
                    bool added = FlowAlgorithm.TryAddElement0(_cachedFirstElement);
                    if (added)
                    {
                        _cachedFirstElement = null;
                    }
                }
            }
        }
예제 #5
0
        private void MakeAnchor(
            VirtualizingLayoutContext context,
            int index,
            Size availableSize)
        {
            _elementManager.ClearRealizedRange();
            // FlowLayout requires that the anchor is the first element in the row.
            var internalAnchor = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement(index, availableSize, context);

            // No need to set the position of the anchor.
            // (0,0) is fine for now since the extent can
            // grow in any direction.
            for (int dataIndex = internalAnchor.Index; dataIndex < index + 1; ++dataIndex)
            {
                var element = context.GetOrCreateElementAt(dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                element.Measure(_algorithmCallbacks.Algorithm_GetMeasureSize(dataIndex, availableSize, context));
                _elementManager.Add(element, dataIndex);
            }
        }
예제 #6
0
        private int GetAnchorIndex(
            Size availableSize,
            bool isWrapping,
            double minItemSpacing,
            string layoutId)
        {
            int anchorIndex    = -1;
            var anchorPosition = new Point();
            var context        = _context;

            if (!IsVirtualizingContext)
            {
                // Non virtualizing host, start generating from the element 0
                anchorIndex = context.ItemCount > 0 ? 0 : -1;
            }
            else
            {
                bool isRealizationWindowConnected = _elementManager.IsWindowConnected(RealizationRect, _orientation.ScrollOrientation, _scrollOrientationSameAsFlow);
                // Item spacing and size in non-virtualizing direction change can cause elements to reflow
                // and get a new column position. In that case we need the anchor to be positioned in the
                // correct column.
                bool needAnchorColumnRevaluation = isWrapping && (
                    _orientation.Minor(_lastAvailableSize) != _orientation.Minor(availableSize) ||
                    _lastItemSpacing != minItemSpacing ||
                    _collectionChangePending);

                var suggestedAnchorIndex = _context.RecommendedAnchorIndex;

                var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 &&
                                              _elementManager.IsDataIndexRealized(suggestedAnchorIndex);

                if (isAnchorSuggestionValid)
                {
                    anchorIndex = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement(
                        suggestedAnchorIndex,
                        availableSize,
                        context).Index;

                    if (_elementManager.IsDataIndexRealized(anchorIndex))
                    {
                        var anchorBounds = _elementManager.GetLayoutBoundsForDataIndex(anchorIndex);
                        if (needAnchorColumnRevaluation)
                        {
                            // We were provided a valid anchor, but its position might be incorrect because for example it is in
                            // the wrong column. We do know that the anchor is the first element in the row, so we can force the minor position
                            // to start at 0.
                            anchorPosition = _orientation.MinorMajorPoint(0, _orientation.MajorStart(anchorBounds));
                        }
                        else
                        {
                            anchorPosition = new Point(anchorBounds.X, anchorBounds.Y);
                        }
                    }
                    else
                    {
                        // It is possible to end up in a situation during a collection change where GetAnchorForTargetElement returns an index
                        // which is not in the realized range. Eg. insert one item at index 0 for a grid layout.
                        // SuggestedAnchor will be 1 (used to be 0) and GetAnchorForTargetElement will return 0 (left most item in row). However 0 is not in the
                        // realized range yet. In this case we realize the gap between the target anchor and the suggested anchor.
                        int firstRealizedDataIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);

                        for (int i = firstRealizedDataIndex - 1; i >= anchorIndex; --i)
                        {
                            _elementManager.EnsureElementRealized(false /*forward*/, i, layoutId);
                        }

                        var anchorBounds = _elementManager.GetLayoutBoundsForDataIndex(suggestedAnchorIndex);
                        anchorPosition = _orientation.MinorMajorPoint(0, _orientation.MajorStart(anchorBounds));
                    }
                }
                else if (needAnchorColumnRevaluation || !isRealizationWindowConnected)
                {
                    // The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window
                    // but not the visible window. In that situation, we still need to produce a valid anchor.
                    var anchorInfo = _algorithmCallbacks.Algorithm_GetAnchorForRealizationRect(availableSize, context);
                    anchorIndex    = anchorInfo.Index;
                    anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset);
                }
                else
                {
                    // No suggestion - just pick first in realized range
                    anchorIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);
                    var firstElementBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
                    anchorPosition = new Point(firstElementBounds.X, firstElementBounds.Y);
                }
            }

            _firstRealizedDataIndexInsideRealizationWindow = _lastRealizedDataIndexInsideRealizationWindow = anchorIndex;
            if (_elementManager.IsIndexValidInData(anchorIndex))
            {
                if (!_elementManager.IsDataIndexRealized(anchorIndex))
                {
                    // Disconnected, throw everything and create new anchor
                    _elementManager.ClearRealizedRange();

                    var anchor = _context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                    _elementManager.Add(anchor, anchorIndex);
                }

                var anchorElement = _elementManager.GetRealizedElement(anchorIndex);
                var desiredSize   = MeasureElement(anchorElement, anchorIndex, availableSize, _context);
                var layoutBounds  = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height);
                _elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds);
            }
            else
            {
                _elementManager.ClearRealizedRange();
            }

            // TODO: Perhaps we can track changes in the property setter
            _lastAvailableSize = availableSize;
            _lastItemSpacing   = minItemSpacing;

            return(anchorIndex);
        }
예제 #7
0
        internal void RecycleElementAt(int index)
        {
            var element = _context.GetOrCreateElementAt(index);

            _context.RecycleElement(element);
        }
예제 #8
0
        /// <inheritdoc />
        protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize)
        {
            if (context.ItemCount > 0)
            {
                var parentMeasure     = new UvMeasure(Orientation, finalSize.Width, finalSize.Height);
                var spacingMeasure    = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
                var realizationBounds = new UvBounds(Orientation, context.RealizationRect);

                var state = (WrapLayoutState)context.LayoutState;
                bool Arrange(WrapItem item, bool isLast = false)
                {
                    if (item.Measure.HasValue == false)
                    {
                        return(false);
                    }

                    if (item.Position == null)
                    {
                        return(false);
                    }

                    var desiredMeasure = item.Measure.Value;

                    if (desiredMeasure.U == 0)
                    {
                        return(true); // if an item is collapsed, avoid adding the spacing
                    }

                    UvMeasure position = item.Position.Value;

                    // Stretch the last item to fill the available space
                    if (isLast)
                    {
                        desiredMeasure.U = parentMeasure.U - position.U;
                    }

                    if (((position.V + desiredMeasure.V) >= realizationBounds.VMin) && (position.V <= realizationBounds.VMax))
                    {
                        // place the item
                        var child = context.GetOrCreateElementAt(item.Index);
                        if (Orientation == Orientation.Horizontal)
                        {
                            child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V));
                        }
                        else
                        {
                            child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U));
                        }
                    }
                    else if (position.V > realizationBounds.VMax)
                    {
                        return(false);
                    }

                    return(true);
                }

                for (var i = 0; i < context.ItemCount; i++)
                {
                    bool continueArranging = Arrange(state.GetItemAt(i));
                    if (continueArranging == false)
                    {
                        break;
                    }
                }
            }

            return(finalSize);
        }
예제 #9
0
        /// <inheritdoc />
        protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
        {
            var totalMeasure      = UvMeasure.Zero;
            var parentMeasure     = new UvMeasure(Orientation, availableSize.Width, availableSize.Height);
            var spacingMeasure    = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
            var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
            var position          = UvMeasure.Zero;

            var state = (WrapLayoutState)context.LayoutState;

            if (state.Orientation != Orientation)
            {
                state.SetOrientation(Orientation);
            }

            if (spacingMeasure.Equals(state.Spacing) == false)
            {
                state.ClearPositions();
                state.Spacing = spacingMeasure;
            }

            if (state.AvailableU != parentMeasure.U)
            {
                state.ClearPositions();
                state.AvailableU = parentMeasure.U;
            }

            double currentV = 0;

            for (int i = 0; i < context.ItemCount; i++)
            {
                bool     measured = false;
                WrapItem item     = state.GetItemAt(i);
                if (item.Measure == null)
                {
                    item.Element = context.GetOrCreateElementAt(i);
                    item.Element.Measure(availableSize);
                    item.Measure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height);
                    measured     = true;
                }

                UvMeasure currentMeasure = item.Measure.Value;
                if (currentMeasure.U == 0)
                {
                    continue; // ignore collapsed items
                }

                if (item.Position == null)
                {
                    if (parentMeasure.U < position.U + currentMeasure.U)
                    {
                        // New Row
                        position.U  = 0;
                        position.V += currentV + spacingMeasure.V;
                        currentV    = 0;
                    }

                    item.Position = position;
                }

                position = item.Position.Value;

                double vEnd = position.V + currentMeasure.V;
                if (vEnd < realizationBounds.VMin)
                {
                    // Item is "above" the bounds
                    if (item.Element != null)
                    {
                        context.RecycleElement(item.Element);
                        item.Element = null;
                    }
                }
                else if (position.V > realizationBounds.VMax)
                {
                    // Item is "below" the bounds.
                    if (item.Element != null)
                    {
                        context.RecycleElement(item.Element);
                        item.Element = null;
                    }

                    // We don't need to measure anything below the bounds
                    break;
                }
                else if (measured == false)
                {
                    // Always measure elements that are within the bounds
                    item.Element = context.GetOrCreateElementAt(i);
                    item.Element.Measure(availableSize);

                    currentMeasure = new UvMeasure(Orientation, item.Element.DesiredSize.Width, item.Element.DesiredSize.Height);
                    if (currentMeasure.Equals(item.Measure) == false)
                    {
                        // this item changed size; we need to recalculate layout for everything after this
                        state.RemoveFromIndex(i + 1);
                        item.Measure = currentMeasure;

                        // did the change make it go into the new row?
                        if (parentMeasure.U < position.U + currentMeasure.U)
                        {
                            // New Row
                            position.U  = 0;
                            position.V += currentV + spacingMeasure.V;
                            currentV    = 0;
                        }

                        item.Position = position;
                    }
                }

                position.U += currentMeasure.U + spacingMeasure.U;
                currentV    = Math.Max(currentMeasure.V, currentV);
            }

            // update value with the last line
            // if the the last loop is(parentMeasure.U > currentMeasure.U + lineMeasure.U) the total isn't calculated then calculate it
            // if the last loop is (parentMeasure.U > currentMeasure.U) the currentMeasure isn't added to the total so add it here
            // for the last condition it is zeros so adding it will make no difference
            // this way is faster than an if condition in every loop for checking the last item
            totalMeasure.U = parentMeasure.U;
            totalMeasure.V = state.GetHeight();

            totalMeasure.U = Math.Ceiling(totalMeasure.U);

            return(Orientation == Orientation.Horizontal ? new Size(totalMeasure.U, totalMeasure.V) : new Size(totalMeasure.V, totalMeasure.U));
        }