Tirer pour actualiser

La commande Tirer pour actualiser permet à l’utilisateur de dérouler une liste de données à l’aide de la fonction tactile, afin de récupérer des données supplémentaires. Cette commande est largement utilisée sur les appareils dotés d’un écran tactile. Vous pouvez utiliser les API indiquées ici afin d’implémenter le modèle Tirer pour actualiser dans votre application.

Gif Tirer pour actualiser

Est-ce le contrôle approprié ?

Utilisez la commande Tirer pour actualiser lorsque vous avez une liste ou une grille de données que l’utilisateur est susceptible de vouloir actualiser régulièrement et si votre application est susceptible de s’exécuter sur des appareils à écran tactile.

Vous pouvez également utiliser RefreshVisualizer pour créer une expérience cohérente d’actualisation appelée d’autres manières, par exemple par un bouton Actualiser.

Contrôles d’actualisation

La commande Tirer pour actualiser est activée par deux contrôles.

  • RefreshContainer - ContentControl qui fournit un wrapper pour l’expérience Tirer pour actualiser. Il gère les interactions tactiles et l’état de son visualiseur d’actualisation interne.
  • RefreshVisualizer - Encapsule la visualisation d’actualisation expliquée dans la section suivante.

Le contrôle principal est RefreshContainer, que vous placez comme wrapper autour du contenu que l’utilisateur tire (pull) pour déclencher une actualisation. RefreshContainer fonctionne uniquement avec une interaction tactile. Nous vous recommandons donc de mettre également un bouton Actualiser à disposition des utilisateurs qui n’ont pas d’interface tactile. Vous pouvez placer le bouton Actualiser dans un emplacement approprié de l’application, soit dans une barre de commandes, soit dans un emplacement proche de la zone en cours d’actualisation.

Visualisation d’actualisation

La visualisation d’actualisation par défaut est un compteur de progression circulaire utilisé pour indiquer quand une actualisation se produit, ainsi que sa progression une fois qu’elle est lancée. Le visualiseur d’actualisation peut avoir 5 états.

La distance dont l’utilisateur a besoin pour tirer (pull) une liste vers le bas en vue de lancer une actualisation s’appelle le seuil. L’état du visualiseur est déterminé par l’état du tirage (pull) par rapport à ce seuil. Les valeurs possibles sont contenues dans l’énumération RefreshVisualizerState.

Idle

L’état par défaut du visualiseur est Inactif. L’utilisateur n’interagit pas avec le RefreshContainer via l’interaction tactile, et aucune actualisation n’est en cours.

Visuellement, rien n’indique la présence d’un visualiseur d’actualisation.

Interaction

Lorsque l’utilisateur tire la liste dans la direction spécifiée par la propriété PullDirection, et avant que le seuil ne soit atteint, le visualiseur se trouve dans l’état Interaction.

  • Si l’utilisateur relâche le contrôle dans cet état, le contrôle retourne à l’état Inactif.

    Seuil préalable de Tirer pour actualiser

    Visuellement, l’icône apparaît désactivée (opacité de 60 %). En outre, l’icône effectue une rotation complète avec l’action de défilement.

  • Si l’utilisateur tire (pull) la liste au-delà du seuil, le visualiseur passe de l’état Interaction à l’état En attente.

    Tirer pour actualiser au niveau du seuil

    Visuellement, l’icône passe à 100 % d’opacité et augmente en taille par impulsions jusqu’à 150 %, puis revient à une taille de 100 % lors de la transition.

Pending

Lorsque l’utilisateur a tiré la liste au-delà du seuil, le visualiseur n’est plus dans l’état En attente.

  • Si l’utilisateur redéplace la liste au-dessus du seuil sans la relâcher, le visualiseur retourne à l’état Interaction.
  • Si l’utilisateur relâche la liste, une requête d’actualisation est envoyée, et l’état passe à Actualisation.

Seuil postérieur de Tirer pour actualiser

Visuellement, l’icône est à 100 % en taille et en opacité. Dans cet état, l’icône continue à se déplacer vers le bas avec l’action de défilement, mais elle ne tourne plus.

Actualisation

Lorsque l’utilisateur relâche le visualiseur au-delà du seuil, le visualiseur est à l’état Actualisation.

Lorsque cet état est entré, l’événement RefreshRequested est déclenché. Il s’agit du signal pour démarrer l’actualisation du contenu de l’application. Les arguments d’événement (RefreshRequestedEventArgs) contiennent un objet Deferral, auquel vous devez prendre un handle dans le gestionnaire d’événements. Ensuite, vous devez marquer le report comme terminé une fois que l’exécution de votre code d’actualisation est terminée.

Lorsque l’actualisation est terminée, le visualiseur retourne à l’état Inactif.

Visuellement, l’icône revient se fixer à l’emplacement du seuil et tourne pendant la durée de l’actualisation. Cette rotation est utilisée pour montrer la progression de l’actualisation, et est remplacée par l’animation du contenu entrant.

Aperçu

Lorsque l’utilisateur tire (pull) dans la direction de l’actualisation à partir d’une position de départ où une actualisation n’est pas autorisée, le visualiseur passe à l’état Aperçu. Cela se produit généralement lorsque le ScrollViewer n’est pas en position 0 quand l’utilisateur commence à tirer.

  • Si l’utilisateur relâche le contrôle dans cet état, le contrôle retourne à l’état Inactif.

Sens du tirage

Par défaut, l’utilisateur tire (pull) une liste de haut en bas pour lancer une actualisation. Si vous disposez d’une liste ou d’une grille avec une orientation différente, vous devez modifier le sens du tirage du conteneur d’actualisation pour les faire correspondre.

La propriété PullDirection prend l’une des valeurs RefreshPullDirection suivantes : BottomToTop, TopToBottom, RightToLeft ou LeftToRight.

Lorsque vous modifiez la direction de l’extraction, la position de départ de la rotation de progression du visualiseur pivote automatiquement de sorte que la flèche démarre à la position appropriée pour le sens d’extraction. Si nécessaire, vous pouvez modifier la propriété RefreshVisualizer.Orientation pour remplacer le comportement automatique. Dans la plupart des cas, nous recommandons de laisser la valeur par défaut, c’est-à-dire, Automatique.

UWP et WinUI 2

Important

Les informations et les exemples de cet article sont optimisés pour les applications qui utilisent le SDK d'application Windows et WinUI 3, mais qui s’appliquent généralement aux applications UWP qui utilisent WinUI 2. Consultez la référence API de la plateforme Windows universelle pour obtenir des informations et des exemples spécifiques à la plateforme.

Cette section contient les informations dont vous avez besoin pour utiliser le contrôle dans une application de la plateforme Windows universelle ou de WinUI 2.

Les contrôles d’actualisation des applications UWP sont inclus dans la bibliothèque d’interface utilisateur Windows 2. Pour plus d’informations, notamment des instructions d’installation, consultez la bibliothèque d’interface utilisateur Windows. Les API de ce contrôle existent dans les espaces de noms Windows.UI.Xaml.Controls (UWP) et Microsoft.UI.Xaml.Controls (WinUI).

Nous vous recommandons d’utiliser la dernière version de WinUI 2 pour obtenir les styles, modèles et fonctionnalités les plus récents pour tous les contrôles.

Pour utiliser le code de cet article avec WinUI 2, utilisez un alias en XAML (nous utilisons muxc) pour représenter les API de bibliothèque d’interface utilisateur Windows incluses dans votre projet. Consultez Bien démarrer avec WinUI 2 pour plus d’informations.

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

<muxc:RefreshContainer />

Implémenter Tirer pour actualiser

L’application WinUI 3 Gallery comprend des exemples interactifs de la plupart des contrôles et des fonctionnalités WinUI 3. Procurez-vous l’application sur le Microsoft Store ou le code source sur GitHub.

Seules quelques étapes sont nécessaires pour ajouter la fonctionnalité Tirer pour actualiser à une liste.

  1. Wrappez votre liste dans un contrôle RefreshContainer.
  2. Gérez l’événement RefreshRequested pour actualiser votre contenu.
  3. Si vous le souhaitez, lancez une actualisation en appelant RequestRefresh (par exemple, à partir d’un clic sur un bouton).

Notes

Vous pouvez instancier un RefreshVisualizer seul. Toutefois, nous vous recommandons de wrapper votre contenu dans un RefreshContainer et d’utiliser le RefreshVisualizer fourni par la propriété RefreshContainer.Visualizer, même pour des scénarios non tactiles. Dans cet article, nous partons du principe que le visualiseur s’obtient toujours à partir du conteneur d’actualisation.

En outre, pour des raisons pratiques, utilisez les membres RequestRefresh et RefreshRequested du conteneur d’actualisation. refreshContainer.RequestRefresh() équivaut à refreshContainer.Visualizer.RequestRefresh() et déclenche à la fois l’événement RefreshContainer.RefreshRequested et les événements RefreshVisualizer.RefreshRequested.

Demander une actualisation

Le conteneur d’actualisation gère les interactions tactiles pour permettre à un utilisateur d’actualiser le contenu via l’interaction tactile. Nous vous recommandons de fournir d’autres affordances pour les interfaces non tactiles, comme un bouton d’actualisation ou un contrôle vocal.

Pour lancer une actualisation, appelez la méthode RequestRefresh.

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

Lorsque vous appelez RequestRefresh, l’état du visualiseur passe directement de Inactif à Actualisation.

Gérer une requête d’actualisation

Pour obtenir un contenu actualisé si nécessaire, gérez l’événement RefreshRequested. Dans le gestionnaire d’événement, vous aurez besoin d’un code spécifique à votre application pour obtenir le contenu actualisé.

Les arguments d’événement (RefreshRequestedEventArgs) contiennent un objet Deferral. Obtenez un handle pour le report dans le gestionnaire d’événements. Ensuite, une fois que l’exécution du code permettant d’effectuer l’actualisation est terminée, marquez le report comme terminé.

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

Répondre aux changements d’état

Vous pouvez répondre aux changements d’état du visualiseur, si nécessaire. Par exemple, pour éviter plusieurs requêtes d’actualisation, vous pouvez désactiver le bouton d’actualisation pendant l’actualisation du visualiseur.

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

Utilisation d’un ScrollViewer dans un RefreshContainer

Notes

Le contenu d’un RefreshContainer doit être un contrôle avec défilement, par exemple ScrollViewer, GridView, ListView, etc. Définir le contenu sur un contrôle de type Grille provoquera un comportement non défini.

Cet exemple montre comment utiliser la commande Tirer pour actualiser avec une visionneuse à défilement.

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

    </ScrollViewer>
</RefreshContainer>

Ajout de la commande Tirer pour actualiser à un affichage Liste

Cet exemple montre comment utiliser la commande Tirer pour actualiser avec un affichage Liste.

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

Obtenir l’exemple de code