// 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; } }