Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I detect a ScrollViewer is being stretched past its available extent

Detecting when a ScrollViewer is either at its top or bottom is easy, but I want to detect when the user is pulling the ScrollViewer even more, past its limits, and some empty spacing appears at the top or bottom. You guessed it, I want to implement something similar to a "pull to refresh".

The VerticalOffset doesn't change, the ViewChanging or ViewChanged events don't fire, I cannot see any transform object changing on child elements. All I know is that it's the ItemsPresenter inside the ScrollContentPresenter that seems to move down.

like image 717
Martin Plante Avatar asked Nov 20 '25 21:11

Martin Plante


1 Answers

Here's a solution which doesn't implement any custom classes, and has practically no code-behind (you could squeeze it down to a single function if you were really code-behind averse).

It does almost exactly the same thing implemented by the ZoomFactor solution posted before.

There are subtleties and limitations to this code.

The most important being that a scroll viewer does not move at all (no stretch interaction) when the internally displayed panel is smaller than the scroll view itself. This is a problem because it breaks the pull the refresh paradigm a bit, but can be worked around by padding the stack panel.

Another important point is the border sizes. The reader can test what happens when the threshold value does not take into account borders. This is also why I've put those hideous borders in the first place, so that this issue can be seen.

A final limitation is the size of the pull down margin. Its height is limited by the actual snap point distances and this is related to the elements in the stack panel. There's a bit of playing around to be done there, but keeping with the working example set here, it shouldn't be too big of a deal.

All of this being said, here is the fully functioning pull down to refresh code:

<RelativePanel Background="Gray">
    <ScrollViewer Name="outerScroll"
                  VerticalSnapPointsAlignment="Far"
                  VerticalSnapPointsType="Mandatory"
                  Width="500"
                  Height="500"
                  BorderBrush="Black"
                  BorderThickness="3"
                  RelativePanel.AlignHorizontalCenterWithPanel="True"
                  RelativePanel.AlignVerticalCenterWithPanel="True">


            <StackPanel Grid.Row="1"
                        x:Name="innerPanel"
                        BorderBrush="Yellow"
                        BorderThickness="4"
                    Width="400"
                      Margin="0,90,0,0"  
                    Background="Blue">

                <TextBlock Height="100" Margin="10,0,10,0" Style="{StaticResource SubheaderTextBlockStyle}">Item 1</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0" Style="{StaticResource SubheaderTextBlockStyle}">Item 2</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0" Style="{StaticResource SubheaderTextBlockStyle}">Item 3</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 4</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 5</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 6</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 8</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 9</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 10</TextBlock>
                <TextBlock Height="100" Margin="10,0,10,0"  Style="{StaticResource SubheaderTextBlockStyle}">Item 11</TextBlock>
        </StackPanel>
    </ScrollViewer>
</RelativePanel>

The contained element needs to implement the IScrollSnapPointsInfo interface. StackPanel does that.

public sealed partial class BlankPage1 : Page
{
    double thresholdValue = 0;
    public BlankPage1()
    {
        this.InitializeComponent();

        outerScroll.ViewChanging += OuterScroll_ViewChanging;

        thresholdValue = innerPanel.Margin.Top + innerPanel.BorderThickness.Top + outerScroll.BorderThickness.Top;

        outerScroll.SizeChanged += (s, e) => { outerScroll.ScrollToVerticalOffset(thresholdValue); };
    }

    private void OuterScroll_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
    {
        if ( e.NextView.VerticalOffset < thresholdValue)
        {
            outerScroll.VerticalSnapPointsType = SnapPointsType.Mandatory;    
        }
        else
            outerScroll.VerticalSnapPointsType = SnapPointsType.None;

        if (e.NextView.VerticalOffset == 0 && !e.IsInertial )
        {
            // Pull to refresh event
            innerPanel.Background = new SolidColorBrush(Colors.Blue);
        }
        else
        {
            // base state
            innerPanel.Background = new SolidColorBrush(Colors.Red);
        }
    }
}
like image 116
MB. Avatar answered Nov 23 '25 15:11

MB.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!