Exemple #1
0
        /// <summary>
        /// A copy of the <see cref="WrapPanel.LayoutLine"/> method, but using the <see cref="ItemProvider"/>
        /// via the <see cref="GetItem"/> method for item retrieval.
        /// </summary>
        protected void LayoutLine(PointF pos, LineMeasurement line)
        {
            float offset = 0;

            for (int i = line.StartIndex; i <= line.EndIndex; i++)
            {
                FrameworkElement layoutChild      = GetItem(i, ItemProvider, true);
                SizeF            desiredChildSize = layoutChild.DesiredSize;
                SizeF            size;
                PointF           location;

                if (Orientation == Orientation.Horizontal)
                {
                    size     = new SizeF(desiredChildSize.Width, line.TotalExtendsInNonOrientationDirection);
                    location = new PointF(pos.X + offset, pos.Y);
                    ArrangeChildVertical(layoutChild, layoutChild.VerticalAlignment, ref location, ref size);
                    offset += desiredChildSize.Width;
                }
                else
                {
                    size     = new SizeF(line.TotalExtendsInNonOrientationDirection, desiredChildSize.Height);
                    location = new PointF(pos.X, pos.Y + offset);
                    ArrangeChildHorizontal(layoutChild, layoutChild.HorizontalAlignment, ref location, ref size);
                    offset += desiredChildSize.Height;
                }

                layoutChild.Arrange(new RectangleF(location.X, location.Y, size.Width, size.Height));

                _arrangedItems[i] = layoutChild;
            }
        }
Exemple #2
0
        protected void LayoutLine(IList <FrameworkElement> children, PointF pos, LineMeasurement line)
        {
            float offset = 0;

            for (int i = line.StartIndex; i <= line.EndIndex; i++)
            {
                FrameworkElement layoutChild      = children[i];
                SizeF            desiredChildSize = layoutChild.DesiredSize;
                SizeF            size;
                PointF           location;

                if (Orientation == Orientation.Horizontal)
                {
                    size     = new SizeF(desiredChildSize.Width, line.TotalExtendsInNonOrientationDirection);
                    location = new PointF(pos.X + offset, pos.Y);
                    ArrangeChildVertical(layoutChild, layoutChild.VerticalAlignment, ref location, ref size);
                    offset += desiredChildSize.Width;
                }
                else
                {
                    size     = new SizeF(line.TotalExtendsInNonOrientationDirection, desiredChildSize.Height);
                    location = new PointF(pos.X, pos.Y + offset);
                    ArrangeChildHorizontal(layoutChild, layoutChild.HorizontalAlignment, ref location, ref size);
                    offset += desiredChildSize.Height;
                }

                layoutChild.Arrange(SharpDXExtensions.CreateRectangleF(location, size));
            }
        }
Exemple #3
0
        public virtual bool FocusPageRight()
        {
            if (Orientation == Orientation.Vertical)
            {
                FrameworkElement currentElement = GetFocusedElementOrChild();
                if (currentElement == null)
                {
                    return(false);
                }

                IList <FrameworkElement> visibleChildren = GetVisibleChildren();
                if (visibleChildren.Count == 0)
                {
                    return(false);
                }
                IList <LineMeasurement> lines = new List <LineMeasurement>(_arrangedLines);
                if (lines.Count == 0)
                {
                    return(false);
                }
                int lastVisibleLineIndex = _actualLastVisibleLineIndex;
                CalcHelper.Bound(ref lastVisibleLineIndex, 0, lines.Count - 1);
                LineMeasurement lastVisibleLine = lines[lastVisibleLineIndex];
                float           limitPosition   = ActualPosition.X + (float)ActualWidth; // Initialize as if an element inside our visible range is focused - then, we must move to the first element
                for (int childIndex = lastVisibleLine.StartIndex; childIndex <= lastVisibleLine.EndIndex; childIndex++)
                {
                    FrameworkElement child = visibleChildren[childIndex];
                    if (child == null)
                    {
                        return(false);
                    }
                    if (!InVisualPath(child, currentElement))
                    {
                        continue;
                    }
                    // One of the elements at the bottom is focused - move one page down
                    limitPosition = child.ActualPosition.X + (float)ActualWidth;
                    break;
                }
                int lastMinusOne = lastVisibleLineIndex - 1;
                CalcHelper.Bound(ref lastMinusOne, 0, lines.Count - 1);
                FrameworkElement nextElement;
                while ((nextElement = FindNextFocusElement(lines.Skip(lastMinusOne).SelectMany(
                                                               line => visibleChildren.Skip(line.StartIndex).Take(line.EndIndex - line.StartIndex + 1)),
                                                           currentElement.ActualBounds, MoveFocusDirection.Right)) != null && (nextElement.ActualBounds.Right < limitPosition - DELTA_DOUBLE))
                {
                    currentElement = nextElement;
                }
                return(currentElement.TrySetFocus(true));
            }
            return(false);
        }
Exemple #4
0
        protected override SizeF CalculateInnerDesiredSize(SizeF totalSize)
        {
            FrameworkElementCollection children = Children;

            lock (Children.SyncRoot)
            {
                if (_newItemProvider != null)
                {
                    if (children.Count > 0)
                    {
                        children.Clear(false);
                    }
                    if (_itemProvider != null)
                    {
                        MPF.TryCleanupAndDispose(_itemProvider);
                    }
                    _itemProvider      = _newItemProvider;
                    _newItemProvider   = null;
                    _updateRenderOrder = true;
                }
                _assumedLineExtendsInNonOrientationDirection = 0;
                IItemProvider itemProvider = ItemProvider;
                if (itemProvider == null)
                {
                    return(base.CalculateInnerDesiredSize(totalSize));
                }
                int numItems = itemProvider.NumItems;
                if (numItems == 0)
                {
                    return(new SizeF());
                }

                // CalculateInnerDesiredSize is called before ArrangeChildren!
                // under the precondition that all items use the same template and are equally sized
                // calulate just one line to find number of items and required size of a line
                LineMeasurement exemplaryLine = _firstArrangedLineIndex < 0 ? CalculateLine(0, totalSize, false) : _arrangedLines[_firstArrangedLineIndex];
                _assumedLineExtendsInNonOrientationDirection = exemplaryLine.TotalExtendsInNonOrientationDirection;
                var itemsPerLine = exemplaryLine.EndIndex - exemplaryLine.StartIndex + 1;
                var estimatedExtendsInNonOrientationDirection = (float)Math.Ceiling((float)numItems / itemsPerLine) * _assumedLineExtendsInNonOrientationDirection;

                return(Orientation == Orientation.Horizontal ? new SizeF(exemplaryLine.TotalExtendsInOrientationDirection, estimatedExtendsInNonOrientationDirection) :
                       new SizeF(estimatedExtendsInNonOrientationDirection, exemplaryLine.TotalExtendsInOrientationDirection));
            }
        }
Exemple #5
0
        protected override SizeF CalculateInnerDesiredSize(SizeF totalSize)
        {
            IList <FrameworkElement> visibleChildren = GetVisibleChildren();
            int numVisibleChildren = visibleChildren.Count;

            if (numVisibleChildren == 0)
            {
                return(new SizeF());
            }
            float totalDesiredWidth  = 0;
            float totalDesiredHeight = 0;
            int   index = 0;

            while (index < numVisibleChildren)
            {
                LineMeasurement line = CalculateLine(visibleChildren, index, totalSize, false);
                if (line.EndIndex < line.StartIndex)
                {
                    // Element doesn't fit
                    break;
                }
                switch (Orientation)
                {
                case Orientation.Horizontal:
                    if (line.TotalExtendsInOrientationDirection > totalDesiredWidth)
                    {
                        totalDesiredWidth = line.TotalExtendsInOrientationDirection;
                    }
                    totalDesiredHeight += line.TotalExtendsInNonOrientationDirection;
                    break;

                case Orientation.Vertical:
                    if (line.TotalExtendsInOrientationDirection > totalDesiredHeight)
                    {
                        totalDesiredHeight = line.TotalExtendsInOrientationDirection;
                    }
                    totalDesiredWidth += line.TotalExtendsInNonOrientationDirection;
                    break;
                }
                index = line.EndIndex + 1;
            }
            return(new SizeF(totalDesiredWidth, totalDesiredHeight));
        }
Exemple #6
0
        protected void AddFocusedElementRange(IList <FrameworkElement> availableElements, RectangleF?startingRect,
                                              int firstLineIndex, int lastLineIndex, int linesBefore, int linesAfter, ICollection <FrameworkElement> outElements)
        {
            IList <LineMeasurement> lines = new List <LineMeasurement>(_arrangedLines);
            int numLines = lines.Count;

            if (numLines == 0)
            {
                return;
            }
            CalcHelper.Bound(ref firstLineIndex, 0, numLines - 1);
            CalcHelper.Bound(ref lastLineIndex, 0, numLines - 1);
            if (linesBefore > 0)
            {
                // Find children before the first index which have focusable elements.
                int formerNumElements = outElements.Count;
                for (int lineIndex = firstLineIndex - 1; lineIndex >= 0; lineIndex--)
                {
                    LineMeasurement line = lines[lineIndex];
                    for (int childIndex = line.StartIndex; childIndex <= line.EndIndex; childIndex++)
                    {
                        FrameworkElement fe = availableElements[childIndex];
                        fe.AddPotentialFocusableElements(startingRect, outElements);
                    }
                    if (formerNumElements == outElements.Count)
                    {
                        continue;
                    }
                    // Found focusable elements
                    linesBefore--;
                    if (linesBefore == 0)
                    {
                        break;
                    }
                    formerNumElements = outElements.Count;
                }
            }
            for (int lineIndex = firstLineIndex; lineIndex <= lastLineIndex; lineIndex++)
            {
                LineMeasurement line = lines[lineIndex];
                for (int childIndex = line.StartIndex; childIndex <= line.EndIndex; childIndex++)
                {
                    FrameworkElement fe = availableElements[childIndex];
                    fe.AddPotentialFocusableElements(startingRect, outElements);
                }
            }
            if (linesAfter > 0)
            {
                // Find children after the last index which have focusable elements.
                int formerNumElements = outElements.Count;
                for (int lineIndex = lastLineIndex + 1; lineIndex < lines.Count; lineIndex++)
                {
                    LineMeasurement line = lines[lineIndex];
                    for (int childIndex = line.StartIndex; childIndex <= line.EndIndex; childIndex++)
                    {
                        FrameworkElement fe = availableElements[childIndex];
                        fe.AddPotentialFocusableElements(startingRect, outElements);
                    }
                    if (formerNumElements == outElements.Count)
                    {
                        continue;
                    }
                    // Found focusable elements
                    linesAfter--;
                    if (linesAfter == 0)
                    {
                        break;
                    }
                    formerNumElements = outElements.Count;
                }
            }
        }
Exemple #7
0
        protected virtual void ArrangeChildren()
        {
            bool fireScrolled = false;

            _totalHeight = 0;
            _totalWidth  = 0;

            IList <FrameworkElement> visibleChildren = GetVisibleChildren();
            int numVisibleChildren = visibleChildren.Count;

            if (numVisibleChildren > 0)
            {
                PointF actualPosition = ActualPosition;
                SizeF  actualSize     = new SizeF((float)ActualWidth, (float)ActualHeight);

                // For Orientation == vertical, this is ActualHeight, for horizontal it is ActualWidth
                float actualExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, actualSize);
                // For Orientation == vertical, this is ActualWidth, for horizontal it is ActualHeight
                float actualExtendsInNonOrientationDirection = GetExtendsInNonOrientationDirection(Orientation, actualSize);
                // Hint: We cannot skip the arrangement of lines above _actualFirstVisibleLineIndex or below _actualLastVisibleLineIndex
                // because the rendering and focus system also needs the bounds of the currently invisible children
                float startPosition = 0;
                // If set to true, we'll check available space from the last to first visible child.
                // That is necessary if we want to scroll a specific child to the last visible position.
                bool invertLayouting = false;
                lock (_renderLock)
                    if (_pendingScrollIndex.HasValue)
                    {
                        fireScrolled = true;
                        int pendingSI = _pendingScrollIndex.Value;
                        if (_scrollToFirst)
                        {
                            _actualFirstVisibleLineIndex = pendingSI;
                        }
                        else
                        {
                            _actualLastVisibleLineIndex = pendingSI;
                            invertLayouting             = true;
                        }
                        _pendingScrollIndex = null;
                    }

                _arrangedLines = new List <LineMeasurement>();
                int index = 0;
                while (index < numVisibleChildren)
                {
                    LineMeasurement line = CalculateLine(visibleChildren, index, _innerRect.Size, false);
                    _arrangedLines.Add(line);
                    index = line.EndIndex + 1;
                }

                // 1) Calculate scroll indices
                if (_doScroll)
                { // Calculate last visible child
                    float spaceLeft = actualExtendsInNonOrientationDirection;
                    if (invertLayouting)
                    {
                        CalcHelper.Bound(ref _actualLastVisibleLineIndex, 0, _arrangedLines.Count - 1);
                        _actualFirstVisibleLineIndex = _actualLastVisibleLineIndex + 1;
                        while (_actualFirstVisibleLineIndex > 0)
                        {
                            LineMeasurement line = _arrangedLines[_actualFirstVisibleLineIndex - 1];
                            spaceLeft -= line.TotalExtendsInNonOrientationDirection;
                            if (spaceLeft + DELTA_DOUBLE < 0)
                            {
                                break;
                            }
                            _actualFirstVisibleLineIndex--;
                        }

                        if (spaceLeft > 0)
                        { // Correct the last scroll index to fill the available space
                            int maxArrangedLine = _arrangedLines.Count - 1;
                            while (_actualLastVisibleLineIndex < maxArrangedLine)
                            {
                                LineMeasurement line = _arrangedLines[_actualLastVisibleLineIndex + 1];
                                spaceLeft -= line.TotalExtendsInNonOrientationDirection;
                                if (spaceLeft + DELTA_DOUBLE < 0)
                                {
                                    break; // Found item which is not visible any more
                                }
                                _actualLastVisibleLineIndex++;
                            }
                        }
                    }
                    else
                    {
                        CalcHelper.Bound(ref _actualFirstVisibleLineIndex, 0, _arrangedLines.Count - 1);
                        _actualLastVisibleLineIndex = _actualFirstVisibleLineIndex - 1;
                        while (_actualLastVisibleLineIndex < _arrangedLines.Count - 1)
                        {
                            LineMeasurement line = _arrangedLines[_actualLastVisibleLineIndex + 1];
                            spaceLeft -= line.TotalExtendsInNonOrientationDirection;
                            if (spaceLeft + DELTA_DOUBLE < 0)
                            {
                                break;
                            }
                            _actualLastVisibleLineIndex++;
                        }

                        if (spaceLeft > 0)
                        { // Correct the first scroll index to fill the available space
                            while (_actualFirstVisibleLineIndex > 0)
                            {
                                LineMeasurement line = _arrangedLines[_actualFirstVisibleLineIndex - 1];
                                spaceLeft -= line.TotalExtendsInNonOrientationDirection;
                                if (spaceLeft + DELTA_DOUBLE < 0)
                                {
                                    break; // Found item which is not visible any more
                                }
                                _actualFirstVisibleLineIndex--;
                            }
                        }
                    }
                }
                else
                {
                    _actualFirstVisibleLineIndex = 0;
                    _actualLastVisibleLineIndex  = _arrangedLines.Count - 1;
                }

                // 2) Calculate start position
                for (int i = 0; i < _actualFirstVisibleLineIndex; i++)
                {
                    LineMeasurement line = _arrangedLines[i];
                    startPosition -= line.TotalExtendsInNonOrientationDirection;
                }

                // 3) Arrange children
                if (Orientation == Orientation.Vertical)
                {
                    _totalHeight = actualExtendsInOrientationDirection;
                }
                else
                {
                    _totalWidth = actualExtendsInOrientationDirection;
                }
                PointF position = Orientation == Orientation.Vertical ?
                                  new PointF(actualPosition.X + startPosition, actualPosition.Y) :
                                  new PointF(actualPosition.X, actualPosition.Y + startPosition);
                foreach (LineMeasurement line in _arrangedLines)
                {
                    LayoutLine(visibleChildren, position, line);

                    startPosition += line.TotalExtendsInNonOrientationDirection;
                    if (Orientation == Orientation.Vertical)
                    {
                        position     = new PointF(actualPosition.X + startPosition, actualPosition.Y);
                        _totalWidth += line.TotalExtendsInNonOrientationDirection;
                    }
                    else
                    {
                        position      = new PointF(actualPosition.X, actualPosition.Y + startPosition);
                        _totalHeight += line.TotalExtendsInNonOrientationDirection;
                    }
                }
            }
            else
            {
                _actualFirstVisibleLineIndex = 0;
                _actualLastVisibleLineIndex  = -1;
            }
            if (fireScrolled)
            {
                InvokeScrolled();
            }
        }
Exemple #8
0
        protected LineMeasurement CalculateLine(IList <FrameworkElement> children, int startIndex, SizeF?measureSize, bool invertLayoutDirection)
        {
            LineMeasurement result = LineMeasurement.Create();

            if (invertLayoutDirection)
            {
                result.EndIndex = startIndex;
            }
            else
            {
                result.StartIndex = startIndex;
            }
            result.TotalExtendsInNonOrientationDirection = 0;
            int   numChildren     = children.Count;
            int   directionOffset = invertLayoutDirection ? -1 : 1;
            float offsetInOrientationDirection  = 0;
            float extendsInOrientationDirection = measureSize.HasValue ? GetExtendsInOrientationDirection(Orientation, measureSize.Value) : float.NaN;
            int   currentIndex = startIndex;

            for (; invertLayoutDirection ? (currentIndex >= 0) : (currentIndex < numChildren); currentIndex += directionOffset)
            {
                FrameworkElement child = children[currentIndex];
                SizeF            desiredChildSize;
                if (measureSize.HasValue)
                {
                    desiredChildSize = measureSize.Value;
                    child.Measure(ref desiredChildSize);
                }
                else
                {
                    desiredChildSize = child.DesiredSize;
                }
                float lastOffsetInOrientationDirection = offsetInOrientationDirection;
                offsetInOrientationDirection += GetExtendsInOrientationDirection(Orientation, desiredChildSize);
                if (!float.IsNaN(extendsInOrientationDirection) && offsetInOrientationDirection > extendsInOrientationDirection)
                {
                    if (invertLayoutDirection)
                    {
                        result.StartIndex = currentIndex + 1;
                    }
                    else
                    {
                        result.EndIndex = currentIndex - 1;
                    }
                    result.TotalExtendsInOrientationDirection = lastOffsetInOrientationDirection;
                    return(result);
                }
                if (desiredChildSize.Height > result.TotalExtendsInNonOrientationDirection)
                {
                    result.TotalExtendsInNonOrientationDirection = GetExtendsInNonOrientationDirection(Orientation, desiredChildSize);
                }
            }
            if (invertLayoutDirection)
            {
                result.StartIndex = currentIndex + 1;
            }
            else
            {
                result.EndIndex = currentIndex - 1;
            }
            result.TotalExtendsInOrientationDirection = offsetInOrientationDirection;
            return(result);
        }
    /// <summary>
    /// A copy of the <see cref="WrapPanel.LayoutLine"/> method, but using the <see cref="ItemProvider"/>
    /// via the <see cref="GetItem"/> method for item retrieval.
    /// </summary>
    protected void LayoutLine(PointF pos, LineMeasurement line)
    {
      float offset = 0;
      for (int i = line.StartIndex; i <= line.EndIndex; i++)
      {
        FrameworkElement layoutChild = GetItem(i, ItemProvider, true);
        SizeF desiredChildSize = layoutChild.DesiredSize;
        SizeF size;
        PointF location;

        if (Orientation == Orientation.Horizontal)
        {
          size = new SizeF(desiredChildSize.Width, line.TotalExtendsInNonOrientationDirection);
          location = new PointF(pos.X + offset, pos.Y);
          ArrangeChildVertical(layoutChild, layoutChild.VerticalAlignment, ref location, ref size);
          offset += desiredChildSize.Width;
        }
        else
        {
          size = new SizeF(line.TotalExtendsInNonOrientationDirection, desiredChildSize.Height);
          location = new PointF(pos.X, pos.Y + offset);
          ArrangeChildHorizontal(layoutChild, layoutChild.HorizontalAlignment, ref location, ref size);
          offset += desiredChildSize.Height;
        }

        layoutChild.Arrange(new RectangleF(location.X, location.Y, size.Width, size.Height));

        _arrangedItems[i] = layoutChild;
      }
    }
Exemple #10
0
        public override bool FocusPageDown()
        {
            IItemProvider itemProvider = ItemProvider;

            if (itemProvider == null)
            {
                return(base.FocusPageDown());
            }

            if (Orientation == Orientation.Horizontal)
            {
                FrameworkElement currentElement = GetFocusedElementOrChild();
                if (currentElement == null)
                {
                    return(false);
                }
                if (itemProvider.NumItems == 0)
                {
                    return(false);
                }
                IList <LineMeasurement> lines = new List <LineMeasurement>(_arrangedLines);
                if (lines.Count == 0)
                {
                    return(false);
                }
                int lastVisibleLineIndex = _actualLastVisibleLineIndex;
                CalcHelper.Bound(ref lastVisibleLineIndex, 0, lines.Count - 1);
                LineMeasurement lastVisibleLine = lines[lastVisibleLineIndex];
                float           limitPosition   = ActualPosition.Y + (float)ActualHeight; // Initialize as if an element inside our visible range is focused - then, we must move to the last element
                for (int childIndex = lastVisibleLine.StartIndex; childIndex <= lastVisibleLine.EndIndex; childIndex++)
                {
                    FrameworkElement child = _arrangedItems[childIndex];
                    if (!InVisualPath(child, currentElement))
                    {
                        continue;
                    }

                    // One of the elements at the bottom is focused - move one page down
                    // so the current last visible line is then the first visible line
                    // the new line and item to select might not yet have been arranged
                    // calculate the position by assuming all lines have equal amount of items

                    // go as many lines down as fill one page (minus 1)
                    int numLinesToGoDown         = (int)Math.Floor(ActualHeight / lastVisibleLine.TotalExtendsInNonOrientationDirection) - 1;
                    int highestPossibleLineIndex = itemProvider.NumItems / (lastVisibleLine.EndIndex - lastVisibleLine.StartIndex + 1);
                    if (lastVisibleLineIndex + numLinesToGoDown > highestPossibleLineIndex)
                    {
                        numLinesToGoDown = highestPossibleLineIndex - lastVisibleLineIndex;
                    }
                    int lineToScrollTo = lastVisibleLineIndex + numLinesToGoDown;
                    SetScrollIndex(lineToScrollTo, false);
                    // try to select a child at same horizontal position
                    int childIndexToSelect = childIndex + ((lastVisibleLine.EndIndex - lastVisibleLine.StartIndex + 1) * numLinesToGoDown);
                    if (childIndexToSelect > itemProvider.NumItems - 1)
                    {
                        childIndexToSelect = itemProvider.NumItems - 1;
                    }
                    FrameworkElement item = GetItem(childIndexToSelect, itemProvider, false);
                    if (item != null)
                    {
                        item.SetFocusPrio = SetFocusPriority.Default;
                    }
                    return(true);
                }
                // select item on same page in last visible line at same horizontal position
                int lastMinusOne = lastVisibleLineIndex - 1;
                CalcHelper.Bound(ref lastMinusOne, 0, lines.Count - 1);
                FrameworkElement nextElement;
                while ((nextElement = FindNextFocusElement(lines.Skip(lastMinusOne).SelectMany(
                                                               line => _arrangedItems.Skip(line.StartIndex).Take(line.EndIndex - line.StartIndex + 1)),
                                                           currentElement.ActualBounds, MoveFocusDirection.Down)) != null && (nextElement.ActualBounds.Bottom < limitPosition + DELTA_DOUBLE))
                {
                    currentElement = nextElement;
                }
                return(currentElement.TrySetFocus(true));
            }
            return(false);
        }
Exemple #11
0
        public override bool FocusPageUp()
        {
            IItemProvider itemProvider = ItemProvider;

            if (itemProvider == null)
            {
                return(base.FocusPageUp());
            }

            if (Orientation == Orientation.Horizontal)
            {
                FrameworkElement currentElement = GetFocusedElementOrChild();
                if (currentElement == null)
                {
                    return(false);
                }
                if (itemProvider.NumItems == 0)
                {
                    return(false);
                }
                IList <LineMeasurement> lines = new List <LineMeasurement>(_arrangedLines);
                if (lines.Count == 0)
                {
                    return(false);
                }
                int             firstVisibleLineIndex = _actualFirstVisibleLineIndex;
                LineMeasurement firstVisibleLine      = lines[firstVisibleLineIndex];
                float           limitPosition         = ActualPosition.Y; // Initialize as if an element inside our visible range is focused - then, we must move to the first element
                for (int childIndex = firstVisibleLine.StartIndex; childIndex <= firstVisibleLine.EndIndex; childIndex++)
                {
                    FrameworkElement child = _arrangedItems[childIndex];
                    if (!InVisualPath(child, currentElement))
                    {
                        continue;
                    }
                    // One of the topmost elements is focused - move one page up
                    // a) how many lines to go up?
                    int numLinesToGoUp = (int)Math.Floor(ActualHeight / firstVisibleLine.TotalExtendsInNonOrientationDirection) - 1;
                    if (numLinesToGoUp < 1)
                    {
                        numLinesToGoUp = 1;
                    }
                    // b) what child to select?
                    int childIndexToSelect = childIndex - ((firstVisibleLine.EndIndex - firstVisibleLine.StartIndex + 1) * numLinesToGoUp);
                    if (childIndexToSelect < 0)
                    {
                        childIndexToSelect = 0;
                    }
                    // c) line index to scroll to
                    int lineToScrollTo = firstVisibleLineIndex - numLinesToGoUp;
                    if (lineToScrollTo < 0)
                    {
                        lineToScrollTo = 0;
                    }
                    SetScrollIndex(lineToScrollTo, true);
                    FrameworkElement item = GetItem(childIndexToSelect, itemProvider, false);
                    if (item != null)
                    {
                        item.SetFocusPrio = SetFocusPriority.Default;
                    }
                    return(true);
                }
                FrameworkElement nextElement;
                while ((nextElement = FindNextFocusElement(lines.Skip(firstVisibleLineIndex).Take(1).SelectMany(
                                                               line => _arrangedItems.Skip(line.StartIndex).Take(line.EndIndex - line.StartIndex + 1)),
                                                           currentElement.ActualBounds, MoveFocusDirection.Up)) != null && (nextElement.ActualPosition.Y > limitPosition - DELTA_DOUBLE))
                {
                    currentElement = nextElement;
                }
                return(currentElement.TrySetFocus(true));
            }
            return(false);
        }
Exemple #12
0
        protected override void ArrangeChildren()
        {
            bool fireScrolled = false;

            lock (Children.SyncRoot)
            {
                if (ItemProvider == null)
                {
                    base.ArrangeChildren();
                    return;
                }

                _totalHeight = 0;
                _totalWidth  = 0;
                int numItems = ItemProvider.NumItems;
                if (numItems > 0)
                {
                    PointF actualPosition = ActualPosition;
                    SizeF  actualSize     = new SizeF((float)ActualWidth, (float)ActualHeight);

                    // For Orientation == vertical, this is ActualHeight, for horizontal it is ActualWidth
                    float actualExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, actualSize);
                    // For Orientation == vertical, this is ActualWidth, for horizontal it is ActualHeight
                    float actualExtendsInNonOrientationDirection = GetExtendsInNonOrientationDirection(Orientation, actualSize);
                    // Hint: We cannot skip the arrangement of lines above _actualFirstVisibleLineIndex or below _actualLastVisibleLineIndex
                    // because the rendering and focus system also needs the bounds of the currently invisible children
                    float startPosition = 0;
                    // If set to true, we'll check available space from the last to first visible child.
                    // That is necessary if we want to scroll a specific child to the last visible position.
                    bool invertLayouting = false;
                    lock (_renderLock)
                        if (_pendingScrollIndex.HasValue)
                        {
                            fireScrolled = true;
                            int pendingSI = _pendingScrollIndex.Value;
                            if (_scrollToFirst)
                            {
                                _actualFirstVisibleLineIndex = pendingSI;
                            }
                            else
                            {
                                _actualLastVisibleLineIndex = pendingSI;
                                invertLayouting             = true;
                            }
                            _pendingScrollIndex = null;
                        }

                    int itemsPerLine = 0;
                    // if we haven't arranged any lines previously - items per line can't be calculated yet, but is not needed
                    if (_firstArrangedLineIndex >= 0)
                    {
                        // to calculate the starting index of elements for a line we assume that every line (until the last) has the same number of items
                        itemsPerLine = _arrangedLines[_firstArrangedLineIndex].EndIndex - _arrangedLines[_firstArrangedLineIndex].StartIndex + 1;
                        _assumedLineExtendsInNonOrientationDirection = _arrangedLines[_firstArrangedLineIndex].TotalExtendsInNonOrientationDirection;
                    }

                    // scrolling may have set an invalid index for the first visible line
                    if (_actualFirstVisibleLineIndex < 0)
                    {
                        _actualFirstVisibleLineIndex = 0;
                    }
                    if (itemsPerLine > 0)
                    {
                        int linesPerPage = (int)Math.Floor(actualExtendsInNonOrientationDirection / _assumedLineExtendsInNonOrientationDirection);
                        int maxLineIndex = (int)Math.Ceiling((float)numItems / itemsPerLine);
                        if (_actualFirstVisibleLineIndex > maxLineIndex - linesPerPage)
                        {
                            _actualFirstVisibleLineIndex = Math.Max(maxLineIndex - linesPerPage, 0);
                        }
                    }

                    // clear values from previous arrange
                    _arrangedItems = new FrameworkElement[numItems];
                    _arrangedLines.Clear();
                    _firstArrangedLineIndex = 0;
                    _lastArrangedLineIndex  = 0;

                    // 1) Calculate scroll indices
                    if (_doScroll)
                    { // Calculate last visible child
                        float spaceLeft = actualExtendsInNonOrientationDirection;
                        if (invertLayouting)
                        {
                            if (_actualLastVisibleLineIndex == int.MaxValue) // when scroll to last item (END) was requested
                            {
                                _actualLastVisibleLineIndex = (int)Math.Ceiling((float)numItems / itemsPerLine) - 1;
                            }
                            _actualFirstVisibleLineIndex = _actualLastVisibleLineIndex + 1;
                            int currentLineIndex = _actualLastVisibleLineIndex;
                            _lastArrangedLineIndex = currentLineIndex;
                            while (_arrangedLines.Count <= currentLineIndex)
                            {
                                _arrangedLines.Add(new LineMeasurement());                                // add "unarranged lines" up to the last visible
                            }
                            int itemIndex             = currentLineIndex * itemsPerLine;
                            int additionalLinesBefore = 0;
                            while (currentLineIndex >= 0 && additionalLinesBefore < NUM_ADD_MORE_FOCUS_LINES)
                            {
                                LineMeasurement line = CalculateLine(itemIndex, _innerRect.Size, false);
                                _arrangedLines[currentLineIndex] = line;

                                _firstArrangedLineIndex = currentLineIndex;

                                currentLineIndex--;
                                itemIndex = line.StartIndex - itemsPerLine;

                                spaceLeft -= line.TotalExtendsInNonOrientationDirection;
                                if (spaceLeft + DELTA_DOUBLE < 0)
                                {
                                    additionalLinesBefore++;
                                }
                                else
                                {
                                    _actualFirstVisibleLineIndex--;
                                }
                            }
                            // now add NUM_ADD_MORE_FOCUS_LINES after last visible
                            itemIndex = _arrangedLines[_lastArrangedLineIndex].EndIndex + 1;
                            int additionalLinesAfterwards = 0;
                            while (itemIndex < numItems && additionalLinesAfterwards < NUM_ADD_MORE_FOCUS_LINES)
                            {
                                LineMeasurement line = CalculateLine(itemIndex, _innerRect.Size, false);
                                _arrangedLines.Add(line);
                                _lastArrangedLineIndex = _arrangedLines.Count - 1;
                                itemIndex = line.EndIndex + 1;
                                additionalLinesAfterwards++;
                            }
                        }
                        else
                        {
                            _actualLastVisibleLineIndex = _actualFirstVisibleLineIndex - 1;
                            _firstArrangedLineIndex     = Math.Max(_actualFirstVisibleLineIndex - NUM_ADD_MORE_FOCUS_LINES, 0);
                            int currentLineIndex = _firstArrangedLineIndex;
                            // add "unarranges lines" up until where we start
                            while (_arrangedLines.Count < currentLineIndex)
                            {
                                _arrangedLines.Add(new LineMeasurement());
                            }
                            int itemIndex = currentLineIndex * itemsPerLine;
                            int additionalLinesAfterwards = 0;
                            while (itemIndex < numItems && additionalLinesAfterwards < NUM_ADD_MORE_FOCUS_LINES)
                            {
                                LineMeasurement line = CalculateLine(itemIndex, _innerRect.Size, false);
                                _arrangedLines.Add(line);

                                _lastArrangedLineIndex = currentLineIndex;

                                currentLineIndex++;
                                itemIndex = line.EndIndex + 1;

                                if (currentLineIndex > _actualFirstVisibleLineIndex)
                                {
                                    spaceLeft -= line.TotalExtendsInNonOrientationDirection;
                                    if (spaceLeft + DELTA_DOUBLE < 0)
                                    {
                                        additionalLinesAfterwards++;
                                    }
                                    else
                                    {
                                        _actualLastVisibleLineIndex++;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        _actualFirstVisibleLineIndex = 0;
                        _actualLastVisibleLineIndex  = _arrangedLines.Count - 1;
                    }

                    // now we know items per line for sure so just calculate it
                    itemsPerLine = _arrangedLines[_firstArrangedLineIndex].EndIndex - _arrangedLines[_firstArrangedLineIndex].StartIndex + 1;
                    _assumedLineExtendsInNonOrientationDirection = _arrangedLines[_firstArrangedLineIndex].TotalExtendsInNonOrientationDirection;

                    // 2) Calculate start position (so the first visible line starts at 0)
                    startPosition -= (_actualFirstVisibleLineIndex - _firstArrangedLineIndex) * _assumedLineExtendsInNonOrientationDirection;

                    // 3) Arrange children
                    if (Orientation == Orientation.Vertical)
                    {
                        _totalHeight = actualExtendsInOrientationDirection;
                    }
                    else
                    {
                        _totalWidth = actualExtendsInOrientationDirection;
                    }
                    PointF position = Orientation == Orientation.Vertical ?
                                      new PointF(actualPosition.X + startPosition, actualPosition.Y) :
                                      new PointF(actualPosition.X, actualPosition.Y + startPosition);
                    foreach (LineMeasurement line in _arrangedLines.Skip(_firstArrangedLineIndex).Take(_lastArrangedLineIndex - _firstArrangedLineIndex + 1))
                    {
                        LayoutLine(position, line);

                        startPosition += line.TotalExtendsInNonOrientationDirection;
                        if (Orientation == Orientation.Vertical)
                        {
                            position = new PointF(actualPosition.X + startPosition, actualPosition.Y);
                        }
                        else
                        {
                            position = new PointF(actualPosition.X, actualPosition.Y + startPosition);
                        }
                    }

                    // estimate the desired size
                    var estimatedExtendsInNonOrientationDirection = (float)Math.Ceiling((float)numItems / itemsPerLine) * _assumedLineExtendsInNonOrientationDirection;
                    if (Orientation == Orientation.Horizontal)
                    {
                        _totalHeight = estimatedExtendsInNonOrientationDirection;
                    }
                    else
                    {
                        _totalWidth = estimatedExtendsInNonOrientationDirection;
                    }

                    // keep one more item, because we did use it in CalcLine (and need always one more to find the last item not fitting on the line)
                    // -> if we dont, it will always be newlyCreated and we keep calling Arrange since the new item recursively sets the parent invalid
                    _itemProvider.Keep(_arrangedLines[_firstArrangedLineIndex].StartIndex, _arrangedLines[_lastArrangedLineIndex].EndIndex + 1);
                }
                else
                {
                    _actualFirstVisibleLineIndex = 0;
                    _actualLastVisibleLineIndex  = -1;
                }
            }
            if (fireScrolled)
            {
                InvokeScrolled();
            }
        }