14

How can I scroll to specific position inside a scrollviewer?

 <ScrollViewer x:Name ="MyScrollView" HorizontalScrollBarVisibility="Hidden" Height="500">                      
   <StackPanel x:Name="ContentsPanel">
        <TextBlock x:Name="someTb" Height="50">
        </TextBlock>
        <TextBlock x:Name="otherTb" Height="100">
        </TextBlock>
   </StackPanel>
</ScrollViewer>

I am trying to scroll to a specific element in my scrollviewer but I am new to UWP and I can't quite get it right how to do it.

I want to set the scroll position of MyScrollView in the second textblock on an event happening.

Nejdi Kroi
  • 612
  • 1
  • 6
  • 17

3 Answers3

28

A better solution is to use ChangeView instead of ScrollToVerticalOffset/ScrollToHorizontalOffset since the latter is obsolete in Windows 10.

MyScrollView.ChangeView(null, abosulatePosition.Y, null, true);

You can even enable scrolling animation by setting the last parameter to false.


Update

For the sake of completion, I've created an extension method for this.

public static void ScrollToElement(this ScrollViewer scrollViewer, UIElement element, 
    bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null)
{
    var transform = element.TransformToVisual((UIElement)scrollViewer.Content);
    var position = transform.TransformPoint(new Point(0, 0));

    if (isVerticalScrolling)
    {
        scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling);
    }
    else
    {
        scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
    }
}

So in this case, just need to call

this.MyScrollView.ScrollToElement(otherTb);
Justin XL
  • 38,763
  • 7
  • 88
  • 133
6

I found the answer

    var transform = otherTb.TransformToVisual(ContentsPanel);
    Point absolutePosition = transform.TransformPoint(new Point(0,0));
    MyScrollView.ScrollToVerticalOffset(absolutePosition.Y);

Update

In UWP ScrollToVerticalOffset is obsolete so

    MyScrollView.ChangeView(null,absolutePosition.Y,null,true)

should be used instead. https://msdn.microsoft.com/en-us/library/windows/apps/dn252763.aspx

Nejdi Kroi
  • 612
  • 1
  • 6
  • 17
-2

Here is a Video demo of the method described below, implemented.

I used to use the ScrollViewerOffsetMediator, an extension method which relied upon the ScrollToVerticalOffset method to smoothly animate scrolling of the ScrollViewer contents. However, ScrollToVerticalOffset has been deprecated in Windows 10, and although it worked in some earlier releases of Windows 10, it no longer does.

The new ChangeView method does not provide either smooth nor controllable animation of the ScrollViewer contents. So here is the solution that I've found:

Place a Grid within the ScrollViewer. Animate the contents of the grid using a RenderTransform. Use the new ChangeView method to set your final desired vertical and horizontal ScrollViewer positions at the time you set up your animation of the grid contents via the transformation. And in your grid transformation, offset the initial values by the final desired ChangeView offset, so that the animation start reference is corrected for the immediate jump that will be caused by the ChangeView method.

XAML:

         <ScrollViewer x:Name="MyScrollView">
            <Grid Name="MyGrid">
                <Grid.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform ScaleX="1" ScaleY="1"/>
                        <TranslateTransform X="0" Y="0"/>
                    </TransformGroup>
                </Grid.RenderTransform>
                <!-- Original ScrollViewer Contents Here... -->
            </Grid>
         </ScrollViewer>

Code:

Public Sub AnimateProperty(Obj As DependencyObject, PropPath As String, StartValue As Double, EndValue As Double, Optional PeriodMS As Integer = 350)

    Dim Storya As New Storyboard

    Dim DA1 As New DoubleAnimationUsingKeyFrames With {.BeginTime = New TimeSpan(0, 0, 0)}

    Storyboard.SetTarget(DA1, Obj)
    Storyboard.SetTargetProperty(DA1, PropPath)


    Dim ddkf1 As New DiscreteDoubleKeyFrame With {.KeyTime = New TimeSpan(0, 0, 0), .Value = StartValue}
    Dim edkf1 As New EasingDoubleKeyFrame With {.Value = EndValue, .KeyTime = New TimeSpan(0, 0, 0, 0, PeriodMS)}

    Dim pe1 As New PowerEase With {.EasingMode = EasingMode.EaseIn}
    edkf1.EasingFunction = pe1


    DA1.KeyFrames.Add(ddkf1)
    DA1.KeyFrames.Add(edkf1)

    Storya.Children.Add(DA1)
    Storya.Begin()

End Sub

Example:

        AnimateProperty(MyGrid, "(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)", 1, 1.4, 350)
        AnimateProperty(MyGrid, "(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)", 1, 1.4, 350)
        AnimateProperty(MyGrid, "(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)", -MyScrollView.VerticalOffset, -120, 350)
        MyScrollView.ChangeView(Nothing, 0, Nothing, True)

In this example, no matter what the initial vertical position is of the ScrollView, the contents will be smoothly animated to a fixed vertical position and zoom.

zax
  • 844
  • 8
  • 14