Partager via


Extraction vers actualisation avec modificateurs sources

Dans cet article, nous allons approfondir l’utilisation d’une fonctionnalité SourceModifier d’InteractionTracker et illustrer son utilisation en créant un contrôle d’extraction à actualiser personnalisé.

Prérequis

Ici, nous partons du principe que vous connaissez les concepts abordés dans ces articles :

Qu’est-ce qu’un SourceModifier et pourquoi sont-ils utiles ?

Comme l’inertieModifiers, sourceModifiers vous donne un contrôle plus fin sur le mouvement d’un InteractionTracker. Mais contrairement aux InertieModifiers qui définissent le mouvement après l’entrée d’InteractionTracker dans l’inertie, Les SourcesModifiers définissent le mouvement alors qu’InteractionTracker est toujours dans son état d’interaction. Dans ces cas, vous souhaitez une expérience différente de la traditionnelle « coller au doigt ».

Un exemple classique est l’expérience de tirage à l’actualisation : lorsque l’utilisateur extrait la liste pour actualiser le contenu et les panoramiques de liste à la même vitesse que le doigt et s’arrête après une certaine distance, le mouvement se sentirait brusque et mécanique. Une expérience plus naturelle serait d’introduire une sensation de résistance alors que l’utilisateur interagit activement avec la liste. Cette petite nuance permet de rendre l’expérience globale de l’utilisateur final d’interagir avec une liste plus dynamique et attrayante. Dans la section Exemple, nous allons en savoir plus sur la façon de générer cette opération.

Il existe 2 types de modificateurs sources :

  • DeltaPosition : est le delta entre la position actuelle du frame et la position précédente du doigt pendant l’interaction tactile du panoramique. Ce modificateur source vous permet de modifier la position delta de l’interaction avant de l’envoyer pour un traitement ultérieur. Il s’agit d’un paramètre de type Vector3 et le développeur peut choisir de modifier l’un des attributs X ou Y ou Z de la position avant de le transmettre à InteractionTracker.
  • DeltaScale : est le delta entre l’échelle actuelle d’images et l’échelle d’image précédente qui a été appliquée pendant l’interaction de zoom tactile. Ce modificateur source vous permet de modifier le niveau de zoom de l’interaction. Il s’agit d’un attribut de type float que le développeur peut modifier avant de le transmettre à InteractionTracker.

Lorsque InteractionTracker est dans son état d’interaction, il évalue chacun des modificateurs sources qui lui sont affectés et détermine si l’un d’eux s’applique. Cela signifie que vous pouvez créer et affecter plusieurs modificateurs sources à un InteractionTracker. Toutefois, lors de la définition de chacune d’elles, vous devez effectuer les opérations suivantes :

  1. Définissez la condition : expression qui définit l’instruction conditionnelle lorsque ce modificateur source spécifique doit être appliqué.
  2. Définissez deltaPosition/DeltaScale : expression de modificateur source qui modifie DeltaPosition ou DeltaScale lorsque la condition définie ci-dessus est remplie.

Exemple

Examinons maintenant comment utiliser des modificateurs sources pour créer une expérience d’extraction à actualisation personnalisée avec un contrôle ListView XAML existant. Nous allons utiliser un canevas comme « Panneau d’actualisation » qui sera empilé sur un ListView XAML pour générer cette expérience.

Pour l’expérience de l’utilisateur final, nous voulons créer l’effet de la « résistance » car l’utilisateur effectue activement un panoramique panoramique de la liste (tactile) et arrêtons le mouvement panoramique une fois que la position dépasse un certain point.

Liste avec extraction à actualiser

Le code de travail de cette expérience se trouve dans le dépôt Windows UI Dev Labs sur GitHub. Voici la procédure pas à pas de la création de cette expérience. Dans votre code de balisage XAML, vous disposez des éléments suivants :

<StackPanel Height="500" MaxHeight="500" x:Name="ContentPanel" HorizontalAlignment="Left" VerticalAlignment="Top" >
 <Canvas Width="400" Height="100" x:Name="RefreshPanel" >
<Image x:Name="FirstGear" Source="ms-appx:///Assets/Loading.png" Width="20" Height="20" Canvas.Left="200" Canvas.Top="70"/>
 </Canvas>
 <ListView x:Name="ThumbnailList"
 MaxWidth="400"
 Height="500"
ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.IsScrollInertiaEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="True" >
 <ListView.ItemTemplate>
 ……
 </ListView.ItemTemplate>
 </ListView>
</StackPanel>

Étant donné que ListView (ThumbnailList) est un contrôle XAML qui fait déjà défiler, vous avez besoin que le défilement soit chaîné à son parent (ContentPanel) lorsqu’il atteint l’élément le plus haut et ne peut plus faire défiler. (ContentPanel est l’emplacement où vous allez appliquer les modificateurs sources.) Pour ce faire, vous devez définir ScrollViewer.IsVerticalScrollChainingEnabled sur true dans le balisage ListView. Vous devez également définir le mode chaînage sur VisualInteractionSource sur Always.

Vous devez définir le gestionnaire PointerPressedEvent avec le paramètre handledEventsToo comme true. Sans cette option, PointerPressedEvent ne sera pas chaîné au ContentPanel, car le contrôle ListView marque ces événements comme gérés et ils ne seront pas envoyés à la chaîne visuelle.

//The PointerPressed handler needs to be added using AddHandler method with the //handledEventsToo boolean set to "true"
//instead of the XAML element's "PointerPressed=Window_PointerPressed",
//because the list view needs to chain PointerPressed handled events as well.
ContentPanel.AddHandler(PointerPressedEvent, new PointerEventHandler( Window_PointerPressed), true);

Maintenant, vous êtes prêt à l’associer à InteractionTracker. Commencez par configurer InteractionTracker, VisualInteractionSource et l’expression qui tireront parti de la position d’InteractionTracker.

// InteractionTracker and VisualInteractionSource setup.
_root = ElementCompositionPreview.GetElementVisual(Root);
_compositor = _root.Compositor;
_tracker = InteractionTracker.Create(_compositor);
_interactionSource = VisualInteractionSource.Create(_root);
_interactionSource.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia;
_interactionSource.PositionYChainingMode = InteractionChainingMode.Always;
_tracker.InteractionSources.Add(_interactionSource);
float refreshPanelHeight = (float)RefreshPanel.ActualHeight;
_tracker.MaxPosition = new Vector3((float)Root.ActualWidth, 0, 0);
_tracker.MinPosition = new Vector3(-(float)Root.ActualWidth, -refreshPanelHeight, 0);

// Use the Tacker's Position (negated) to apply to the Offset of the Image.
// The -{refreshPanelHeight} is to hide the refresh panel
m_positionExpression = _compositor.CreateExpressionAnimation($"-tracker.Position.Y - {refreshPanelHeight} ");
m_positionExpression.SetReferenceParameter("tracker", _tracker);
_contentPanelVisual.StartAnimation("Offset.Y", m_positionExpression);

Avec cette configuration, le panneau d’actualisation est hors de la fenêtre d’affichage dans sa position de départ et tous les utilisateurs voient est listView Lorsque le panoramique atteint le ContentPanel, l’événement PointerPressed est déclenché, où vous demandez au système d’utiliser InteractionTracker pour piloter l’expérience de manipulation.

private void Window_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch) {
 // Tell the system to use the gestures from this pointer point (if it can).
 _interactionSource.TryRedirectForManipulation(e.GetCurrentPoint(null));
 }
}

Remarque

Si le chaînage des événements gérés n’est pas nécessaire, l’ajout du gestionnaire PointerPressedEvent peut être effectué directement via le balisage XAML à l’aide de l’attribut (PointerPressed="Window_PointerPressed").

L’étape suivante consiste à configurer les modificateurs sources. Vous utiliserez 2 modificateurs sources pour obtenir ce comportement ; Résistance et arrêt.

  • Résistance : déplacez deltaPosition.Y à moitié la vitesse jusqu’à ce qu’elle atteigne la hauteur du RefreshPanel.
CompositionConditionalValue resistanceModifier = CompositionConditionalValue.Create (_compositor);
ExpressionAnimation resistanceCondition = _compositor.CreateExpressionAnimation(
 $"-tracker.Position.Y < {pullToRefreshDistance}");
resistanceCondition.SetReferenceParameter("tracker", _tracker);
ExpressionAnimation resistanceAlternateValue = _compositor.CreateExpressionAnimation(
 "source.DeltaPosition.Y / 3");
resistanceAlternateValue.SetReferenceParameter("source", _interactionSource);
resistanceModifier.Condition = resistanceCondition;
resistanceModifier.Value = resistanceAlternateValue;
  • Arrêter : arrêtez le déplacement après l’intégralité de RefreshPanel sur l’écran.
CompositionConditionalValue stoppingModifier = CompositionConditionalValue.Create (_compositor);
ExpressionAnimation stoppingCondition = _compositor.CreateExpressionAnimation(
 $"-tracker.Position.Y >= {pullToRefreshDistance}");
stoppingCondition.SetReferenceParameter("tracker", _tracker);
ExpressionAnimation stoppingAlternateValue = _compositor.CreateExpressionAnimation("0");
stoppingModifier.Condition = stoppingCondition;
stoppingModifier.Value = stoppingAlternateValue;
Now add the 2 source modifiers to the InteractionTracker.
List<CompositionConditionalValue> modifierList = new List<CompositionConditionalValue>()
{ resistanceModifier, stoppingModifier };
_interactionSource.ConfigureDeltaPositionYModifiers(modifierList);

Ce diagramme fournit une visualisation de la configuration de SourceModifiers.

Diagramme panoramique

Maintenant, avec les SourcesModifiers, vous remarquerez lors du mouvement panoramique vers le bas et atteindre l’élément le plus haut, le panneau d’actualisation est extrait à moitié le rythme du panoramique jusqu’à ce qu’il atteigne la hauteur RefreshPanel, puis cesse de se déplacer.

Dans l’exemple complet, une animation de trame clé est utilisée pour faire tourner une icône pendant l’interaction dans le canevas RefreshPanel. Tout contenu peut être utilisé à sa place ou utiliser la position d’InteractionTracker pour piloter cette animation séparément.