Deslizar para actualizar

Deslizar para actualizar permite al usuario desplegar una lista de datos con la función táctil para recuperar más datos. Se usa ampliamente Deslizar para actualizar en dispositivos con pantalla táctil. Puedes usar las API que se muestran aquí para implementar Deslizar para actualizar en la aplicación.

GIF de Deslizar para actualizar

¿Es este el control adecuado?

Usa Deslizar para actualizar cuando tengas una lista o cuadrícula de datos que el usuario quizá quiera actualizar con regularidad y es posible que la aplicación se ejecute en dispositivos básicamente táctiles.

También puedes usar RefreshVisualizer para crear una experiencia coherente de actualización que se invoque de otras maneras, como un botón de actualización.

Controles de actualización

Dos controles habilitan la función Deslizar para actualizar.

  • RefreshContainer: ContentControl que proporciona un contenedor para la experiencia de deslizar para actualizar. Controla las interacciones táctiles y administra el estado de su visualizador de actualización interna.
  • RefreshVisualizer: encapsula la visualización de actualización que se explica en la siguiente sección.

El control principal es RefreshContainer, que se coloca como un contenedor alrededor del contenido que el usuario desliza para activar una actualización. RefreshContainer solo funciona con la función táctil, por lo que recomendamos que también ofrezcas un botón de actualización para los usuarios que no dispongan de interfaz táctil. Puedes colocar el botón Actualizar en una ubicación adecuada en la aplicación, en una barra de comandos o cerca de la superficie que se está actualizando.

Visualización de actualización

La visualización de actualización predeterminada es un indicador giratorio de progreso circular que se usa para comunicarse cuando se va a producir una actualización y durante el progreso de esta una vez iniciada. El visualizador de actualización tiene cinco estados.

La distancia que el usuario necesita para deslizar una lista hacia abajo e iniciar así una actualización se denomina umbral. El visualizador State viene determinado por el estado de extracción correspondiente a este umbral. Los valores posibles se encuentran en la enumeración RefreshVisualizerState.

Inactivo

El estado predeterminado del visualizador es Idle. El usuario no interactúa con RefreshContainer a través de la función táctil, y no hay una actualización en curso.

Visualmente, no hay ninguna prueba del visualizador de actualización.

Interacción

Cuando el usuario desliza la lista en la dirección especificada por la propiedad PullDirection, y antes de que se alcance el umbral, el visualizador se encuentra en el estado Interacting.

  • Si el usuario suelta el control en este estado, el control vuelve a Idle.

    Deslizar para actualizar antes del umbral

    Visualmente, el icono aparece como deshabilitado (60 % de opacidad). Además, el icono hace una rotación completa con la acción de desplazamiento.

  • Si el usuario desliza la lista más allá del umbral, el visualizador pasa de Interacting a Pending.

    Deslizar para actualizar en el umbral

    Visualmente, el icono cambia a un 100 % de opacidad y su tamaño se amplía hasta un 150 % para volver al 100 % durante la transición.

Pending

Cuando el usuario ha desliza la lista más allá del umbral, el visualizador se encuentra en el estado Pending.

  • Si el usuario vuelve la lista atrás por encima del umbral sin soltarla, vuelve al estado Interacting.
  • Si el usuario suelta la lista, se inicia una solicitud de actualización y pasa al estado Refreshing.

Deslizar para actualizar después del umbral

Visualmente, el icono tiene un 100 % de tamaño y opacidad. En este estado, el icono sigue hacia abajo con la acción de desplazamiento, pero ya no gira.

Actualizando

Cuando el usuario suelta el visualizador más allá del umbral, está en el estado Refreshing.

Cuando se especifica este estado, se genera el evento RefreshRequested. Esta es la señal para que comience la actualización del contenido de la aplicación. Los argumentos del evento (RefreshRequestedEventArgs) contienen un objeto Deferral, que debe tener un manipulador del controlador de eventos. Después, debes marcar el aplazamiento como completado cuando se haya completado el código para realizar la actualización.

Una vez completada la actualización, el visualizador vuelve al estado Idle.

Visualmente, el icono vuelve a la ubicación de umbral y gira durante la actualización. Este giro se usa para mostrar el progreso de la actualización y se reemplaza por la animación del contenido entrante.

Inspección

Cuando el usuario desliza el dedo en la dirección de la actualización desde una posición de inicio donde no se permite una actualización, el visualizador entra en el estado Peeking. Esto suele ocurrir cuando ScrollViewer no está en la posición 0 en el momento en que el usuario inicia la extracción.

  • Si el usuario suelta el control en este estado, el control vuelve a Idle.

Dirección de deslizamiento

De manera predeterminada, el usuario desliza una lista de arriba hacia abajo para iniciar una actualización. Si tienes una lista o cuadrícula con una orientación diferente, debes cambiar la dirección de deslizamiento del contenedor de actualización para que coincida.

La propiedad PullDirection toma uno de estos valores RefreshPullDirection: BottomToTop, TopToBottom, RightToLeft o LeftToRight.

Al cambiar la dirección de deslizamiento, la posición inicial del indicador giratorio de progreso del visualizador rota de manera automática para que la flecha empiece en la posición adecuada para la dirección de deslizamiento. Si es necesario, puedes cambiar la propiedad RefreshVisualizer.Orientation para invalidar el comportamiento automático. En la mayoría de los casos, se recomienda dejar el valor predeterminado Auto.

UWP y WinUI 2

Importante

La información y los ejemplos de este artículo están optimizados para aplicaciones que usan el SDK de Aplicaciones para Windows y WinUI 3, pero generalmente son aplicables a las aplicaciones para UWP que usan WinUI 2. Consulte el material de referencia de las API de UWP para obtener información y ejemplos específicos de la plataforma.

Esta sección contiene información que necesita para usar el control en una aplicación para UWP o WinUI 2.

Los controles de actualización de las aplicaciones para UWP se incluyen como parte de WinUI 2. Para obtener más información e instrucciones sobre la instalación, consulta el artículo WinUI 2. Hay API para este control en los espacios de nombres Windows.UI.Xaml.Controls (UWP) y Microsoft.UI.Xaml.Controls (WinUI).

Se recomienda usar la versión más reciente de WinUI 2 para obtener los estilos, las plantillas y las características más actuales de todos los controles.

Para usar el código de este artículo con WinUI 2, use un alias en XAML (usamos muxc) para representar las API de la Biblioteca de interfaz de usuario de Windows que se incluyen en el proyecto. Consulte Introducción a la Biblioteca de interfaz de usuario de Windows 2 para obtener más información.

xmlns:muxc="using:Microsoft.UI.Xaml.Controls"

<muxc:RefreshContainer />

Implementación de tirar para actualizar

La aplicación WinUI 3 Gallery incluye ejemplos interactivos de la mayoría de los controles, características y funcionalidades de WinUI 3. Obtenga la aplicación de Microsoft Store u obtenga el código fuente en GitHub.

Para agregar a una lista la función Deslizar para actualizar, se necesitan solo unos pocos pasos.

  1. Encapsula la lista en un control RefreshContainer.
  2. Manipula el evento RefreshRequested para actualizar el contenido.
  3. Otra opción es iniciar una actualización mediante una llamada a RequestRefresh (por ejemplo, a través de un clic en un botón).

Nota:

Puedes crear una instancia de un RefreshVisualizer por sí solo, pero te recomendamos que encapsules el contenido en un RefreshContainer y que uses el RefreshVisualizer proporcionado por la propiedad RefreshContainer.Visualizer, incluso en escenarios no táctiles. En este artículo, se supone que el visualizador siempre se obtiene desde el contenedor de la actualización.

Además, se pueden usar los miembros RequestRefresh y RefreshRequested del contenedor de la actualización para mayor comodidad. refreshContainer.RequestRefresh() es equivalente a refreshContainer.Visualizer.RequestRefresh() y generará los eventos RefreshContainer.RefreshRequested y RefreshVisualizer.RefreshRequested.

Solicitud de una actualización

El contenedor de actualización controla las interacciones táctiles para permitir al usuario actualizar el contenido mediante la función táctil. Te recomendamos que proporciones otras prestaciones para las interfaces no táctiles, como un botón Actualizar o un control de voz.

Para iniciar una actualización, llama al método RequestRefresh.

// See the Examples section for the full code.
private void RefreshButtonClick(object sender, RoutedEventArgs e)
{
    RefreshContainer.RequestRefresh();
}

Cuando se llama a RequestRefresh, el estado del visualizador pasa directamente de Idle a Refreshing.

Manipulación de una solicitud de actualización

Para obtener contenido actualizado cuando sea necesario, manipula el evento RefreshRequested. En el controlador de eventos, necesitarás código específico de la aplicación para obtener el contenido actualizado.

Los argumentos del evento (RefreshRequestedEventArgs) contienen un objeto Deferral. Obtén un manipulador para el aplazamiento en el controlador de eventos. Después, marca el aplazamiento como completado cuando se haya completado el código para realizar la actualización.

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

Respuesta a los cambios de estado

Puedes responder a los cambios de estado del visualizador, si es necesario. Por ejemplo, para evitar varias solicitudes de actualización, puedes deshabilitar un botón Actualizar mientras se está actualizando el visualizador.

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

Uso de un ScrollViewer en un RefreshContainer

Nota:

El contenido de un RefreshContainer debe ser un control desplazable, como ScrollViewer, GridView, ListView, etc. Si se establece el contenido en un control como Grid, se producirá un comportamiento indefinido.

En este ejemplo se muestra cómo usar la función Deslizar para actualizar con un visor de desplazamiento.

<RefreshContainer>
    <ScrollViewer VerticalScrollMode="Enabled"
                  VerticalScrollBarVisibility="Auto"
                  HorizontalScrollBarVisibility="Auto">
 
        <!-- Scrollviewer content -->

    </ScrollViewer>
</RefreshContainer>

Adición de Deslizar para actualizar a ListView

En este ejemplo se muestra cómo se usa la función Deslizar para actualizar con una vista de lista.

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

Obtener el código de ejemplo