如何检测 ScrollViewer 是否超出其可用范围
How can I detect a ScrollViewer is being stretched past its available extent
检测 ScrollViewer 何时位于其顶部或底部很容易,但我想检测用户何时进一步拉动 ScrollViewer,超过其限制,并且顶部或底部出现一些空白间距。你猜对了,我想实现类似于 "pull to refresh".
的东西
VerticalOffset 没有改变,ViewChanging 或 ViewChanged 事件没有触发,我看不到任何变换对象在子元素上发生变化。我只知道是 ScrollContentPresenter 里面的 ItemsPresenter 好像往下移动了。
GitHub 上的 Microsoft UWP 示例中有一个很好的下拉刷新实现示例。
其他很棒的样本是 listed in Samples folder。请注意 XAML 示例名称以 "Xaml" 开头,因此更容易找到它们。 :)
我有一个非常适用于 Scaling/Zooming 的解决方案(pull out go somewhere idiom)但我不确定如何将它应用到 translate idiom(也许是因为我只是厌倦了那一刻)。
我把它写下来,以防有人能找到一种巧妙的方法来为翻译(拉动刷新)问题找到类似的解决方案。
// setup code. I do this when I load content into the scroll viewer initially
myScrollView.MinZoomFactor = desiredMinZoom * 0.4f;
myScrollView.ZoomSnapPoints.Clear();
myScrollView.ZoomSnapPoints.Add((float) desiredMinZoom );
// ...
private void myScrollView_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
if (e.NextView.ZoomFactor < myScrollView.ZoomSnapPoints.First())
myScrollView.ZoomSnapPointsType = SnapPointsType.Mandatory;
else
myScrollView.ZoomSnapPointsType = SnapPointsType.Optional;
if ( e.NextView.ZoomFactor == myScrollView.MinZoomFactor )
{
VisualStateManager.GoToState(this, "GoSomewhere", true);
bookView.ZoomToFactor( myScrollView.MinZoomFactor );
}
}
此代码的行为就好像在 MinZoom 因子下的任何东西实际上都在拉伸 canvas(因为它只是在您放手时快速返回),但是一旦缩放回到"green zone".
它完全符合人们的预期。
这是一个没有实现任何自定义 类 并且几乎没有代码隐藏的解决方案(如果您真的不喜欢代码隐藏,您可以将它压缩成一个函数)。
它所做的事情几乎与之前发布的 ZoomFactor 解决方案所实现的完全相同。
此代码有细微之处和局限性。
最重要的 是当内部显示的面板小于滚动视图本身时,滚动查看器根本不会移动(没有拉伸交互)。这是一个问题,因为它稍微打破了拉刷新模式,但可以通过填充堆栈面板来解决。
另一个重点是边框大小。 reader 可以测试当 threshold
值不考虑边界时会发生什么。这也是为什么我把那些丑陋的边框放在第一位的原因,以便可以看到这个问题。
最后一个限制是下拉边距的大小。它的高度受实际捕捉点距离的限制,这与堆栈面板中的元素有关。那里需要做一些尝试,但与此处设置的工作示例保持一致,应该没什么大不了的。
综上所述,这是功能齐全的下拉刷新代码:
<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>
包含的元素需要实现IScrollSnapPointsInfo
接口。 StackPanel 就是这样做的。
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);
}
}
}
检测 ScrollViewer 何时位于其顶部或底部很容易,但我想检测用户何时进一步拉动 ScrollViewer,超过其限制,并且顶部或底部出现一些空白间距。你猜对了,我想实现类似于 "pull to refresh".
的东西VerticalOffset 没有改变,ViewChanging 或 ViewChanged 事件没有触发,我看不到任何变换对象在子元素上发生变化。我只知道是 ScrollContentPresenter 里面的 ItemsPresenter 好像往下移动了。
GitHub 上的 Microsoft UWP 示例中有一个很好的下拉刷新实现示例。
其他很棒的样本是 listed in Samples folder。请注意 XAML 示例名称以 "Xaml" 开头,因此更容易找到它们。 :)
我有一个非常适用于 Scaling/Zooming 的解决方案(pull out go somewhere idiom)但我不确定如何将它应用到 translate idiom(也许是因为我只是厌倦了那一刻)。
我把它写下来,以防有人能找到一种巧妙的方法来为翻译(拉动刷新)问题找到类似的解决方案。
// setup code. I do this when I load content into the scroll viewer initially
myScrollView.MinZoomFactor = desiredMinZoom * 0.4f;
myScrollView.ZoomSnapPoints.Clear();
myScrollView.ZoomSnapPoints.Add((float) desiredMinZoom );
// ...
private void myScrollView_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
if (e.NextView.ZoomFactor < myScrollView.ZoomSnapPoints.First())
myScrollView.ZoomSnapPointsType = SnapPointsType.Mandatory;
else
myScrollView.ZoomSnapPointsType = SnapPointsType.Optional;
if ( e.NextView.ZoomFactor == myScrollView.MinZoomFactor )
{
VisualStateManager.GoToState(this, "GoSomewhere", true);
bookView.ZoomToFactor( myScrollView.MinZoomFactor );
}
}
此代码的行为就好像在 MinZoom 因子下的任何东西实际上都在拉伸 canvas(因为它只是在您放手时快速返回),但是一旦缩放回到"green zone".
它完全符合人们的预期。
这是一个没有实现任何自定义 类 并且几乎没有代码隐藏的解决方案(如果您真的不喜欢代码隐藏,您可以将它压缩成一个函数)。
它所做的事情几乎与之前发布的 ZoomFactor 解决方案所实现的完全相同。
此代码有细微之处和局限性。
最重要的 是当内部显示的面板小于滚动视图本身时,滚动查看器根本不会移动(没有拉伸交互)。这是一个问题,因为它稍微打破了拉刷新模式,但可以通过填充堆栈面板来解决。
另一个重点是边框大小。 reader 可以测试当 threshold
值不考虑边界时会发生什么。这也是为什么我把那些丑陋的边框放在第一位的原因,以便可以看到这个问题。
最后一个限制是下拉边距的大小。它的高度受实际捕捉点距离的限制,这与堆栈面板中的元素有关。那里需要做一些尝试,但与此处设置的工作示例保持一致,应该没什么大不了的。
综上所述,这是功能齐全的下拉刷新代码:
<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>
包含的元素需要实现IScrollSnapPointsInfo
接口。 StackPanel 就是这样做的。
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);
}
}
}