下拉以刷新
下拉刷新使用户可以使用触控下拉数据列表以检索更多数据。 下拉刷新广泛用于具有触摸屏的设备。 可以使用这里显示的 API 在应用中实现下拉刷新。
这是正确的控件吗?
如果你拥有用户希望定期刷新的数据列表或网格,并且应用可能在触摸优先的设备上运行,请使用下拉刷新。
你也可以使用 RefreshVisualizer 创建以其他方式调用(例如,通过刷新按钮)的一致的刷新体验。
刷新控件
下拉刷新是通过两个控件启用的。
- RefreshContainer - 为下拉刷新体验提供包装器的 ContentControl。 它处理触控交互并管理其内部刷新可视化工具的状态。
- RefreshVisualizer - 封装下一部分中介绍的刷新可视化。
主要控件是 RefreshContainer,作为用户通过下拉以触发刷新的内容的包装器。 RefreshContainer 仅适用于触控,因此建议也要为没有触控界面的用户提供刷新按钮。 可以将刷新按钮放在应用中合适的位置,例如命令栏上或靠近所刷新面的位置。
刷新可视化
默认的刷新可视化是一个环形的进度旋转图标,用于告知用户刷新将发生的时间和刷新启动后的进度。 刷新可视化工具有 5 种状态。
用户需要下拉列表以启动刷新的距离称为阈值。 可视化工具状态是由下拉状态相对于此阈值的关系决定的。 可能的值包含在 RefreshVisualizerState 枚举中。
Idle
可视化工具的默认状态为“空闲”。 用户目前不通过触控与 RefreshContainer 交互,也没有正在进行的刷新。
看不到刷新可视化工具的证据。
正在交互
当用户在 PullDirection 属性指定的方向下拉列表,在达到阈值前,可视化工具处于“正在交互”状态。
如果用户在此状态期间释放控件,控件将返回“空闲”。
可以看到,图标显示为已禁用(60% 不透明度)。 此外,图标随滚动操作旋转全程。
如果用户下拉列表的距离超过阈值,可视化工具会从“正在交互”转换为“挂起”。
可以看到,在转换期间,图标切换到 100% 不透明度,并且脉冲大小增加到 150% 然后又回到 100%。
挂起的
当用户下拉列表的距离超过阈值时,可视化工具会处于“挂起”状态。
- 如果用户向上移动列表回到阈值之上并且不释放列表,列表将返回“正在交互”状态。
- 如果用户释放列表,会启动一个刷新请求并转换为“正在刷新”状态。
可以看到,图标大小和不透明度均为 100%。 在这种状态下,图标继续随滚动操作向下移动,但不再旋转。
正在刷新
当用户释放可视化工具的距离超过阈值时,就会处于“正在刷新”状态。
进入此状态时,会引发 RefreshRequested 事件。 这是应用的内容刷新的启动信号。 事件参数 (RefreshRequestedEventArgs) 包含一个 Deferral 对象,应在事件处理程序中为其提供一个图柄。 然后,当要执行刷新的代码完成后,应将此延迟标记为已完成。
刷新完成后,可视化工具将回到“空闲”状态。
可以看到,图标回到阈值位置并在刷新期间旋转。 此旋转用于显示刷新进度,并将替换为传入内容的动画。
正在扫视
当用户从不允许刷新的起始位置沿刷新方向下拉时,可视化工具会进入“正在扫视”状态。 用户开始下拉时 ScrollViewer 不处于位置 0 时通常会发生这种情况。
- 如果用户在此状态期间释放控件,控件将返回“空闲”。
拉动方向
默认情况下,用户从上到下拉动列表以启动刷新。 如果列表或网格具有不同的方向,则应更改刷新容器的拉动方向以便匹配列表或网格的方向。
PullDirection 属性使用以下 RefreshPullDirection 值之一:BottomToTop、TopToBottom、RightToLeft 或 LeftToRight。
更改拉动方向时,可视化工具的进度旋转图标的起始位置会自动旋转,以便箭头与拉动方向的相应位置对应。 如果需要,可以更改 RefreshVisualizer.Orientation 属性以覆盖自动行为。 在大多数情况下,建议保留默认值 Auto。
UWP 和 WinUI 2
重要
本文中的信息和示例是针对使用 Windows App SDK 和 WinUI 3 的应用优化的,但通常适用于使用 WinUI 2 的 UWP 应用。 有关特定于平台的信息和示例,请查看 UWP API 参考。
本部分包含在 UWP 或 WinUI 2 应用中使用该控件所需的信息。
用于 UWP 应用的刷新控件是 WinUI 2 的一部分。 有关详细信息(包括安装说明),请参阅 WinUI 2。 此控件的 API 存在于 Windows.UI.Xaml.Controls (UWP) 和 Microsoft.UI.Xaml.Controls (WinUI) 命名空间中。
- UWP API:RefreshContainer、RefreshVisualizer
- WinUI 2 API:RefreshContainer、RefreshVisualizer
- 打开 WinUI 2 库应用,了解 PullToRefresh 的实际应用。 WinUI 2 库应用包括大多数 WinUI 2 控件、特性和功能的交互式示例。 通过 Microsoft Store 获取应用,或在 GitHub 上获取源代码。
建议使用最新的 WinUI 2 来获取所有控件的最新样式、模板和功能。
要将本文中的代码与 WinUI 2 配合使用,请使用 XAML 中的别名(我们使用 muxc
)来表示项目中包含的 Windows UI 库 API。 有关详细信息,请参阅 WinUI 2 入门。
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
<muxc:RefreshContainer />
实现下拉刷新
WinUI 3 库应用包括大多数 WinUI 3 控件、特性和功能的交互式示例。 通过 Microsoft Store 获取应用,或在 GitHub 上获取源代码
向列表添加下拉刷新功能只需要几个步骤。
- 将列表包装到一个 RefreshContainer 控件中。
- 处理 RefreshRequested 事件以刷新内容。
- (可选)通过调用 RequestRefresh(例如,从按钮单击)启动刷新。
注意
可以实例化 RefreshVisualizer 本身。 但是,即使针对非触控场景,我们还是建议你将内容包装到一个 RefreshContainer 中并使用 RefreshContainer.Visualizer 属性提供的 RefreshVisualizer。 在本文中,我们假设可视化工具始终是从刷新容器获得的。
此外,为方便期间,应使用刷新容器的 RequestRefresh 和 RefreshRequested 成员。 refreshContainer.RequestRefresh()
与 refreshContainer.Visualizer.RequestRefresh()
等效,任何一个都会同时引发 RefreshContainer.RefreshRequested 事件和 RefreshVisualizer.RefreshRequested 事件。
请求刷新
刷新容器处理触控交互来让用户通过触控刷新内容。 建议提供适用于非触控界面的其他选择,例如刷新按钮或语音控制。
要启动刷新,请调用 RequestRefresh方法。
// See the Examples section for the full code.
private void RefreshButtonClick(object sender, RoutedEventArgs e)
{
RefreshContainer.RequestRefresh();
}
调用 RequestRefresh 时,可视化工具的状态会从“空闲”直接转换为“正在刷新”。
处理刷新请求
要在需要时获取最新内容,请处理 RefreshRequested 事件。 在事件处理程序中,你需要特定于应用的代码来获取最新内容。
事件参数 (RefreshRequestedEventArgs) 包含一个 Deferral 对象。 在事件处理程序中获取延迟的图柄。 然后,当要执行刷新的代码完成后,将此延迟标记为已完成。
// See the Examples section for the full code.
private async void RefreshContainer_RefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
{
// Respond to a request by performing a refresh and using the deferral object.
using (var RefreshCompletionDeferral = args.GetDeferral())
{
// Do some async operation to refresh the content
await FetchAndInsertItemsAsync(3);
// The 'using' statement ensures the deferral is marked as complete.
// Otherwise, you'd call
// RefreshCompletionDeferral.Complete();
// RefreshCompletionDeferral.Dispose();
}
}
响应状态更改
如果需要,你可以响应可视化工具状态的更改。 例如,为防止多个刷新请求,可以在可视化工具刷新时禁用刷新按钮。
// See the Examples section for the full code.
private void Visualizer_RefreshStateChanged(RefreshVisualizer sender, RefreshStateChangedEventArgs args)
{
// Respond to visualizer state changes.
// Disable the refresh button if the visualizer is refreshing.
if (args.NewState == RefreshVisualizerState.Refreshing)
{
RefreshButton.IsEnabled = false;
}
else
{
RefreshButton.IsEnabled = true;
}
}
在 RefreshContainer 中使用 ScrollViewer
注意
RefreshContainer 的内容必须是可滚动控件,如 ScrollViewer、GridView、ListView 等。将内容设置为 Grid 等控件将导致未定义的行为。
本示例介绍如何对滚动查看器使用下拉刷新。
<RefreshContainer>
<ScrollViewer VerticalScrollMode="Enabled"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<!-- Scrollviewer content -->
</ScrollViewer>
</RefreshContainer>
向 ListView 添加下拉刷新
本示例介绍如何对列表视图使用下拉刷新。
<StackPanel Margin="0,40" Width="280">
<CommandBar OverflowButtonVisibility="Collapsed">
<AppBarButton x:Name="RefreshButton" Click="RefreshButtonClick"
Icon="Refresh" Label="Refresh"/>
<CommandBar.Content>
<TextBlock Text="List of items"
Style="{StaticResource TitleTextBlockStyle}"
Margin="12,8"/>
</CommandBar.Content>
</CommandBar>
<RefreshContainer x:Name="RefreshContainer">
<ListView x:Name="ListView1" Height="400">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ListItemData">
<Grid Height="80">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind Path=Header}"
Style="{StaticResource SubtitleTextBlockStyle}"
Grid.Row="0"/>
<TextBlock Text="{x:Bind Path=Date}"
Style="{StaticResource CaptionTextBlockStyle}"
Grid.Row="1"/>
<TextBlock Text="{x:Bind Path=Body}"
Style="{StaticResource BodyTextBlockStyle}"
Grid.Row="2"
Margin="0,4,0,0" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</RefreshContainer>
</StackPanel>
public sealed partial class MainPage : Page
{
public ObservableCollection<ListItemData> Items { get; set; }
= new ObservableCollection<ListItemData>();
public MainPage()
{
this.InitializeComponent();
Loaded += MainPage_Loaded;
ListView1.ItemsSource = Items;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= MainPage_Loaded;
RefreshContainer.RefreshRequested += RefreshContainer_RefreshRequested;
RefreshContainer.Visualizer.RefreshStateChanged += Visualizer_RefreshStateChanged;
// Add some initial content to the list.
await FetchAndInsertItemsAsync(2);
}
private void RefreshButtonClick(object sender, RoutedEventArgs e)
{
RefreshContainer.RequestRefresh();
}
private async void RefreshContainer_RefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
{
// Respond to a request by performing a refresh and using the deferral object.
using (var RefreshCompletionDeferral = args.GetDeferral())
{
// Do some async operation to refresh the content
await FetchAndInsertItemsAsync(3);
// The 'using' statement ensures the deferral is marked as complete.
// Otherwise, you'd call
// RefreshCompletionDeferral.Complete();
// RefreshCompletionDeferral.Dispose();
}
}
private void Visualizer_RefreshStateChanged(RefreshVisualizer sender, RefreshStateChangedEventArgs args)
{
// Respond to visualizer state changes.
// Disable the refresh button if the visualizer is refreshing.
if (args.NewState == RefreshVisualizerState.Refreshing)
{
RefreshButton.IsEnabled = false;
}
else
{
RefreshButton.IsEnabled = true;
}
}
// App specific code to get fresh data.
private async Task FetchAndInsertItemsAsync(int updateCount)
{
for (int i = 0; i < updateCount; ++i)
{
// Simulate delay while we go fetch new items.
await Task.Delay(1000);
Items.Insert(0, GetNextItem());
}
}
private ListItemData GetNextItem()
{
return new ListItemData()
{
Header = "Header " + DateTime.Now.Second.ToString(),
Date = DateTime.Now.ToLongDateString(),
Body = DateTime.Now.ToLongTimeString()
};
}
}
public class ListItemData
{
public string Header { get; set; }
public string Date { get; set; }
public string Body { get; set; }
}
获取示例代码
- WinUI 库示例 - 以交互式格式查看所有 XAML 控件。