예제 #1
0
    // BringIntoView functionality is ported from WinUI ScrollPresenter
    // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/ScrollPresenter/ScrollPresenter.cpp
    // with partial modifications to match the ScrollViewer control behavior.

    protected override void OnBringIntoViewRequested(BringIntoViewRequestedEventArgs args)
    {
        base.OnBringIntoViewRequested(args);

        UIElement content = RealContent as UIElement;

        if (args.Handled ||
            args.TargetElement == this ||
            (args.TargetElement == content && content?.Visibility == Visibility.Collapsed) ||
            !SharedHelpers.IsAncestor(args.TargetElement, this, true /*checkVisibility*/))
        {
            // Ignore the request when:
            // - It was handled already.
            // - The target element is this ScrollPresenter itself. A parent scrollPresenter may fulfill the request instead then.
            // - The target element is effectively collapsed within the ScrollPresenter.
            return;
        }

        Rect   targetRect = new Rect();
        double targetZoomedHorizontalOffset = 0.0;
        double targetZoomedVerticalOffset   = 0.0;
        double appliedOffsetX = 0.0;
        double appliedOffsetY = 0.0;
        var    viewportWidth  = ViewportWidth;
        var    viewportHeight = ViewportHeight;
        var    zoomFactor     = Scroller.ZoomFactor;

        // Compute the target offsets based on the provided BringIntoViewRequestedEventArgs.
        ComputeBringIntoViewTargetOffsets(
            content,
            args,
            out targetZoomedHorizontalOffset,
            out targetZoomedVerticalOffset,
            out appliedOffsetX,
            out appliedOffsetY,
            out targetRect);

        // Do not include the applied offsets so that potential parent bring-into-view contributors ignore that shift.
        Rect nextTargetRect = new Rect(
            targetRect.X * zoomFactor - targetZoomedHorizontalOffset - appliedOffsetX,
            targetRect.Y * zoomFactor - targetZoomedVerticalOffset - appliedOffsetY,
            Math.Min(targetRect.Width * zoomFactor, viewportWidth),
            Math.Min(targetRect.Height * zoomFactor, viewportHeight));

        Rect viewportRect = new Rect(
            0.0f,
            0.0f,
            (float)viewportWidth,
            (float)viewportHeight);

        var verticalOffset         = Scroller.VerticalOffset;
        var horizontalOffset       = Scroller.HorizontalOffset;
        var zoomedVerticalOffset   = verticalOffset;
        var zoomedHorizontalOffset = horizontalOffset;

        if (targetZoomedHorizontalOffset != zoomedHorizontalOffset ||
            targetZoomedVerticalOffset != zoomedVerticalOffset)
        {
            Scroller.ChangeView(targetZoomedHorizontalOffset, targetZoomedVerticalOffset, zoomFactor, !args.AnimationDesired);
        }
        else
        {
            // No offset change was triggered because the target offsets are the same as the current ones. Mark the operation as completed immediately.
            //RaiseViewChangeCompleted(true /*isForScroll*/, ScrollPresenterViewChangeResult::Completed, offsetsChangeCorrelationId);
        }

        if (SharedHelpers.DoRectsIntersect(nextTargetRect, viewportRect))
        {
            // Next bring a portion of this ScrollPresenter into view.
            args.TargetRect       = nextTargetRect;
            args.TargetElement    = Scroller;
            args.HorizontalOffset = args.HorizontalOffset - appliedOffsetX;
            args.VerticalOffset   = args.VerticalOffset - appliedOffsetY;
        }
        else
        {
            // This ScrollPresenter did not even partially bring the TargetRect into its viewport.
            // Mark the operation as handled since no portion of this ScrollPresenter needs to be brought into view.
            args.Handled = true;
        }
    }