Partager via


Dispositions réactives avec XAML

Le système de disposition XAML fournit le dimensionnement automatique des éléments, des panneaux de disposition et des états visuels pour vous aider à créer une interface utilisateur réactive. Grâce à une disposition dynamique, vous pouvez faire en sorte que votre application s’affiche parfaitement avec différentes tailles de fenêtres d’application, résolutions, densités de pixels et orientations. Vous pouvez également utiliser XAML pour repositionner, redimensionner, ajuster dynamiquement, afficher/masquer, remplacer ou remodéliser l’interface utilisateur de votre application, comme indiqué dans Techniques de conception réactive. Ici, nous expliquons comment implémenter des dispositions dynamiques avec XAML.

Dispositions fluides avec propriétés et panneaux

Une disposition dynamique repose principalement sur l’utilisation appropriée de propriétés XAML et de panneaux de disposition pour repositionner, redimensionner et ajuster dynamiquement le contenu d’une manière fluide.

Le système de disposition XAML prend en charge les dispositions statiques et fluides. Dans une disposition statique, vous donnez aux contrôles des tailles et positions de pixels explicites. Lorsque l’utilisateur modifie la résolution ou l’orientation de son appareil, l’interface utilisateur ne change pas. Les dispositions statiques peuvent être découpées sur différents facteurs de forme et tailles d’affichage. D’un autre côté, les dispositions fluides rétrécissent, s’agrandissent et s’ajustent dynamiquement à l’espace visuel disponible sur un appareil.

Dans la pratique, vous utilisez une combinaison d’éléments statiques et fluides pour créer votre interface utilisateur. Vous utilisez toujours des valeurs et des éléments statiques à certains endroits, mais assurez-vous que l’interface utilisateur globale réagit à différentes résolutions, tailles d’écran et vues.

Nous vous expliquons ici comment utiliser les propriétés XAML et les panneaux de disposition pour créer une disposition fluide.

Propriétés de disposition

Les propriétés de disposition contrôlent la taille et la position d’un élément. Pour créer une disposition fluide, utilisez le dimensionnement proportionnel ou automatique pour les éléments, et laissez les panneaux de disposition positionner eux-mêmes leurs enfants selon les besoins.

Voici certaines propriétés de disposition courantes et comment les utiliser pour créer des dispositions fluides.

Height et Width

Les propriétés Height et Width spécifient la taille d’un élément. Vous pouvez utiliser des valeurs fixes mesurées en pixels effectifs, ou utiliser le dimensionnement automatique ou proportionnel.

Le dimensionnement automatique redimensionne les éléments d’interface utilisateur pour qu’ils s’adaptent à leur contenu ou à leur conteneur parent. Vous pouvez également utiliser le dimensionnement automatique avec les lignes et les colonnes d’une grille. Pour utiliser le dimensionnement automatique, définissez la hauteur et/ou la largeur des éléments d’interface utilisateur sur Auto.

Remarque

Le redimensionnement d’un élément à son contenu ou à son conteneur dépend de la façon dont le conteneur parent gère le dimensionnement de ses enfants. Pour plus d’informations, voir Panneaux de disposition plus loin dans cet article.

Le dimensionnement proportionnel répartit l’espace disponible entre les lignes et les colonnes d’une grille par proportions pondérées. En XAML, les valeurs en étoile sont exprimées en * (ou n* pour le dimensionnement pondéré des étoiles). Par exemple, pour spécifier qu’une colonne est 5 fois plus large que la deuxième colonne dans une disposition de 2 colonnes, utilisez « 5* » et « * » pour les propriétés Width dans les éléments ColumnDefinition.

Cet exemple combine le dimensionnement fixe, automatique et proportionnel dans une grille avec 4 colonnes.

Colonne Calibrage Description
Column_1 Automatique La colonne sera dimensionner pour qu’elle corresponde à son contenu.
Column_2 * Une fois les colonnes automatiques calculées, la colonne obtient une partie de la largeur restante. Column_2 sera d’une moitié aussi large que Column_4.
Column_3 44 La colonne sera de 44 pixels de large.
Column_4 2* Une fois les colonnes automatiques calculées, la colonne obtient une partie de la largeur restante. Column_4 sera deux fois plus large que Column_2.

La largeur de colonne par défaut est « * », vous n’avez donc pas besoin de définir explicitement cette valeur pour la deuxième colonne.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="44"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Column 1 sizes to its content." FontSize="24"/>
</Grid>

Dans le concepteur XAML Visual Studio, le résultat ressemble à ceci.

Grille de 4 colonnes dans le concepteur Visual Studio

Pour obtenir la taille d’un élément à l’exécution, utilisez les propriétés en lecture seule ActualHeight et ActualWidth à la place de Height et Width.

Contraintes de taille

Lorsque vous utilisez le dimensionnement automatique dans votre interface utilisateur, vous devrez peut-être toujours placer des contraintes sur la taille d’un élément. Vous pouvez définir les propriétés MinWidth MaxWidth/ et MinHeight/MaxHeight Pour spécifier des valeurs qui limitent la taille d’un élément tout en autorisant le redimensionnement fluide.

Dans une grille, MinWidth/MaxWidth peut également être utilisé avec des définitions de colonnes, et MinHeight/MaxHeight peut être utilisé avec des définitions de lignes.

Alignment

Utilisez les propriétés HorizontalAlignment et VerticalAlignment pour spécifier comment un élément doit être positionné dans son conteneur parent.

  • Les valeurs de HorizontalAlignment sont Left, Center, Right et Stretch.
  • Les valeurs de VerticalAlignment sont Top, Center, Bottom et Stretch.

Avec l’alignement Stretch , les éléments remplissent tout l’espace qu’ils sont fournis dans le conteneur parent. Stretch est la valeur par défaut pour les deux propriétés d’alignement. Toutefois, certains contrôles, comme Button, remplacent cette valeur dans leur style par défaut. Tout élément pouvant avoir des éléments enfants peut traiter la valeur Stretch pour les propriétés HorizontalAlignment et VerticalAlignment de manière unique. Par exemple, un élément utilisant les valeurs Stretch par défaut placées dans une grille s’étire pour remplir la cellule qui la contient. Le même élément placé dans une taille de canevas à son contenu. Pour plus d’informations sur la façon dont chaque panneau gère la valeur Stretch, consultez l’article panneaux De disposition.

Pour plus d’informations, consultez l’article Alignement, marge et remplissage, ainsi que les pages de référence HorizontalAlignment et VerticalAlignment.

Visibilité

Vous pouvez révéler ou masquer un élément en définissant sa propriété Visibility sur l’une des valeurs d’énumération Visibility : Visible ou Réduite. Lorsqu’un élément est Réduit, il ne prend pas d’espace dans la disposition de l’interface utilisateur.

Vous pouvez modifier la propriété Visibility d’un élément dans le code ou dans un état visuel. Lorsque la visibilité d’un élément est modifiée, tous ses éléments enfants sont également modifiés. Vous pouvez remplacer des sections de votre interface utilisateur en révélant un panneau lors de la réduction d’un autre panneau.

Conseil

Lorsque votre interface utilisateur comporte des éléments qui ont la valeur Collapsed par défaut, les objets sont toujours créés au démarrage, même s’ils ne sont pas visibles. Vous pouvez différer le chargement de ces éléments jusqu’à ce qu’ils soient affichés à l’aide de l’attribut x :Load pour retarder la création des objets. Cela peut améliorer les performances de démarrage. Pour plus d’informations, consultez l’attribut x :Load.

Ressources de style

Vous n’avez pas besoin de définir chaque valeur de propriété individuellement sur un contrôle. Il est généralement plus efficace de regrouper les valeurs de propriété dans une ressource Style et d’appliquer le style à un contrôle. Cela est particulièrement vrai lorsque vous devez appliquer les mêmes valeurs de propriété à de nombreux contrôles. Pour plus d’informations sur l’utilisation de styles, consultez Contrôles de style.

Panneaux de disposition

Pour positionner des objets visuels, vous devez les placer dans un panneau ou un autre objet conteneur. L’infrastructure XAML fournit différentes classes de panneau, telles que Canvas, Grid, RelativePanel et StackPanel, qui servent de conteneurs et vous permettent de positionner et d’organiser les éléments d’interface utilisateur au sein d’eux.

La principale chose à prendre en compte lors du choix d’un panneau de disposition est la façon dont le panneau positionne et dimensionne ses éléments enfants. Vous devrez peut-être également tenir compte de la façon dont les éléments enfants qui se chevauchent sont superposés les uns sur les autres.

Voici une comparaison des principales fonctionnalités des contrôles de panneau fournis dans l’infrastructure XAML.

Panel, contrôle Description
Canevas Le canevas ne prend pas en charge l’interface utilisateur fluide ; vous contrôlez tous les aspects du positionnement et du dimensionnement des éléments enfants. Vous l’utilisez généralement pour des cas spéciaux tels que la création de graphiques ou la définition de petites zones statiques d’une interface utilisateur adaptative plus grande. Vous pouvez utiliser du code ou des états visuels pour repositionner des éléments au moment de l’exécution.
  • Les éléments sont positionnés absolument à l’aide de propriétés jointes Canvas.Top et Canvas.Left.
  • La superposition peut être spécifiée explicitement à l’aide de la propriété jointe Canvas.ZIndex.
  • Les valeurs stretch pour HorizontalAlignment/VerticalAlignment sont ignorées. Si la taille d’un élément n’est pas définie explicitement, elle est dimensionne sur son contenu.
  • Le contenu enfant n’est pas visuellement clippé s’il est supérieur au panneau.
  • Le contenu enfant n’est pas limité par les limites du panneau.
  • Grid La grille prend en charge le redimensionnement fluide des éléments enfants. Vous pouvez utiliser du code ou des états visuels pour repositionner et reflower des éléments.
  • Les éléments sont organisés dans des lignes et des colonnes à l’aide des propriétés jointes Grid.Row et Grid.Column.
  • Les éléments peuvent s’étendre sur plusieurs lignes et colonnes à l’aide des propriétés jointes Grid.RowSpan et Grid.ColumnSpan.
  • Les valeurs stretch pour HorizontalAlignment/VerticalAlignment sont respectées. Si la taille d’un élément n’est pas définie explicitement, elle s’étend pour remplir l’espace disponible dans la cellule de grille.
  • Le contenu enfant est visuellement clippé s’il est supérieur au panneau.
  • La taille du contenu est limitée par les limites du panneau. Par conséquent, le contenu défilant affiche les barres de défilement si nécessaire.
  • RelativePanel
  • Les éléments sont disposés par rapport au bord ou au centre du panneau, et par rapport à l’autre.
  • Les éléments sont positionnés à l’aide de diverses propriétés jointes qui alignent le panneau de configuration, l’alignement frère et la position frère.
  • Les valeurs stretch pour HorizontalAlignment/VerticalAlignment sont ignorées, sauf si les propriétés jointes RelativePanel pour l’alignement provoquent l’étirement (par exemple, un élément est aligné à la fois sur les bords droit et gauche du panneau). Si la taille d’un élément n’est pas définie explicitement et qu’elle n’est pas étirée, elle est dimensionnée à son contenu.
  • Le contenu enfant est visuellement clippé s’il est supérieur au panneau.
  • La taille du contenu est limitée par les limites du panneau. Par conséquent, le contenu défilant affiche les barres de défilement si nécessaire.
  • StackPanel
  • Les éléments sont empilés dans une seule ligne verticalement ou horizontalement.
  • Les valeurs Stretch pour HorizontalAlignment/VerticalAlignment sont respectées dans la direction opposée à la propriété Orientation. Si la taille d’un élément n’est pas définie explicitement, elle s’étend pour remplir la largeur disponible (ou la hauteur si l’orientation est horizontale). Dans la direction spécifiée par la propriété Orientation, un élément est dimensionné à son contenu.
  • Le contenu enfant est visuellement clippé s’il est supérieur au panneau.
  • La taille du contenu n’est pas limitée par les limites du panneau dans la direction spécifiée par la propriété Orientation. Par conséquent, le contenu défilant s’étend au-delà des limites du panneau et n’affiche pas les barres de défilement. Vous devez limiter explicitement la hauteur (ou la largeur) du contenu enfant pour afficher ses barres de défilement.
  • VariableSizedWrapGrid
  • Les éléments sont organisés dans des lignes ou des colonnes qui s’encapsulent automatiquement dans une nouvelle ligne ou colonne lorsque la valeur MaximumRowsOrColumns est atteinte.
  • Indique si les éléments sont organisés dans des lignes ou des colonnes sont spécifiés par la propriété Orientation.
  • Les éléments peuvent s’étendre sur plusieurs lignes et colonnes à l’aide des propriétés jointes VariableSizedWrapGrid.RowSpan et VariableSizedWrapGrid.ColumnSpan.
  • Les valeurs Stretch pour HorizontalAlignment et VerticalAlignment sont ignorées. Les éléments sont dimensionnés comme spécifiés par les propriétés ItemHeight et ItemWidth. Si ces propriétés ne sont pas définies, elles prennent leurs valeurs à partir de la taille de la première cellule.
  • Le contenu enfant est visuellement clippé s’il est supérieur au panneau.
  • La taille du contenu est limitée par les limites du panneau. Par conséquent, le contenu défilant affiche les barres de défilement si nécessaire.
  • Pour obtenir des informations détaillées et des exemples de ces panneaux, consultez Panneaux de disposition.

    Les panneaux de disposition vous permettent d’organiser votre interface utilisateur en groupes logiques de contrôles. Lorsque vous les utilisez avec les paramètres de propriété appropriés, vous bénéficiez d’une prise en charge du redimensionnement automatique, du repositionnement et du reflowing des éléments d’interface utilisateur. Toutefois, la plupart des dispositions de l’interface utilisateur ont besoin d’une modification supplémentaire lorsqu’il existe des modifications significatives de la taille de la fenêtre. Pour cela, vous pouvez utiliser des états visuels.

    Dispositions adaptatives avec états visuels et déclencheurs d’état

    Utilisez les états visuels pour apporter des changements significatifs à votre interface utilisateur en fonction de la taille de la fenêtre ou d’autres modifications.

    Lorsque la fenêtre de votre application augmente ou diminue au-delà d’une certaine quantité, vous pouvez modifier les propriétés de disposition pour repositionner, redimensionner, reflower, révéler ou remplacer des sections de votre interface utilisateur. Vous pouvez définir différents états visuels pour votre interface utilisateur et les appliquer lorsque la largeur de la fenêtre ou la hauteur de la fenêtre dépasse un seuil spécifié.

    VisualState définit les valeurs de propriété qui sont appliquées à un élément lorsqu’il est dans un état particulier. Vous regroupez des états visuels dans un VisualStateManager qui applique l’état VisualState approprié lorsque les conditions spécifiées sont remplies. Un AdaptiveTrigger offre un moyen facile de définir le seuil (également appelé « point d’arrêt ») au niveau duquel un état est appliqué en XAML. Ou vous pouvez appeler la méthode VisualStateManager.GoToState dans votre code pour appliquer l’état du visuel. Des exemples de ces deux méthodes sont présentés dans les sections suivantes.

    Définir des états visuels dans le code

    Pour appliquer un état visuel à partir du code, vous appelez la méthode VisualStateManager.GoToState. Par exemple, pour appliquer un état lorsque la fenêtre de l’application est une taille particulière, gérez l’événement SizeChanged et appelez GoToState pour appliquer l’état approprié.

    Ici, un VisualStateGroup contient deux définitions de VisualState. Le premier, DefaultStateest vide. Quand elle est appliquée, les valeurs définies dans la page XAML sont appliquées. Le deuxième, modifie WideStatela propriété DisplayMode du SplitView en inline et ouvre le volet. Cet état est appliqué dans le gestionnaire d’événements SizeChanged si la largeur de la fenêtre est supérieure à 640 pixels effectifs.

    Remarque

    Windows ne permet pas à votre application de détecter l’appareil spécifique sur lequel elle s’exécute. Il peut vous indiquer la famille d’appareils (bureau, etc.) sur laquelle l’application s’exécute, la résolution effective et la quantité d’espace d’écran disponible pour l’application (la taille de la fenêtre de l’application). Nous vous recommandons de définir des états visuels pour les tailles d’écran et points d’arrêt.

    <Page ...
        SizeChanged="CurrentWindow_SizeChanged">
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState x:Name="DefaultState">
                            <Storyboard>
                            </Storyboard>
                        </VisualState>
    
                    <VisualState x:Name="WideState">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.DisplayMode"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <SplitViewDisplayMode>Inline</SplitViewDisplayMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.IsPaneOpen"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    
    private void CurrentWindow_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
        if (e.Size.Width > 640)
            VisualStateManager.GoToState(this, "WideState", false);
        else
            VisualStateManager.GoToState(this, "DefaultState", false);
    }
    
    // YourPage.h
    void CurrentWindow_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs const& e);
    
    // YourPage.cpp
    void YourPage::CurrentWindow_SizeChanged(IInspectable const& sender, SizeChangedEventArgs const& e)
    {
        if (e.NewSize.Width > 640)
            VisualStateManager::GoToState(*this, "WideState", false);
        else
            VisualStateManager::GoToState(*this, "DefaultState", false);
    }
    
    

    Définir des états visuels dans le balisage XAML

    Avant Windows 10, les définitions VisualState nécessitaient des objets Storyboard pour les modifications de propriété et vous deviez appeler GoToState dans le code pour appliquer l’état. Cette caractéristique apparaît dans l'exemple précédent. Vous verrez toujours de nombreux exemples qui utilisent cette syntaxe, ou vous pouvez avoir du code existant qui l’utilise.

    À compter de Windows 10, vous pouvez utiliser la syntaxe Setter simplifiée indiquée ici, et vous pouvez utiliser un StateTrigger dans votre balisage XAML pour appliquer l’état. Vous utilisez des déclencheurs d’état pour créer des règles simples qui déclenchent automatiquement des modifications d’état visuel en réponse à un événement d’application.

    Cet exemple fait la même chose que l’exemple précédent, mais utilise la syntaxe Setter simplifiée au lieu d’un Storyboard pour définir les modifications de propriété. Au lieu d’appeler GoToState, il utilise le déclencheur d’état AdaptiveTrigger intégré pour appliquer l’état. Lorsque vous utilisez des déclencheurs d’état, vous n’avez pas besoin de définir un déclencheur vide DefaultState. Les paramètres par défaut sont réappliqués automatiquement lorsque les conditions du déclencheur d’état ne sont plus remplies.

    <Page ...>
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState>
                        <VisualState.StateTriggers>
                            <!-- VisualState to be triggered when the
                                 window width is >=640 effective pixels. -->
                            <AdaptiveTrigger MinWindowWidth="640" />
                        </VisualState.StateTriggers>
    
                        <VisualState.Setters>
                            <Setter Target="mySplitView.DisplayMode" Value="Inline"/>
                            <Setter Target="mySplitView.IsPaneOpen" Value="True"/>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    

    Important

    Dans l’exemple précédent, la propriété jointe VisualStateManager.VisualStateGroups est définie sur l’élément Grid. Lorsque vous utilisez StateTriggers, vérifiez toujours que VisualStateGroups est attaché au premier enfant de la racine afin que les déclencheurs prennent effet automatiquement. (Ici, Grid est le premier enfant de l’élément Page racine.)

    Syntaxe de propriété jointe

    Dans un VisualState, vous définissez généralement une valeur pour une propriété de contrôle ou pour l’une des propriétés jointes du panneau qui contient le contrôle. Lorsque vous définissez une propriété jointe, utilisez des parenthèses autour du nom de la propriété jointe.

    Cet exemple montre comment définir la propriété jointe RelativePanel.AlignHorizontalCenterWithPanel sur une zone de texte nommée myTextBox. Le premier XAML utilise la syntaxe ObjectAnimationUsingKeyFrames et le second utilise la syntaxe Setter.

    <!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
    <ObjectAnimationUsingKeyFrames
        Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
        Storyboard.TargetName="myTextBox">
        <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
    </ObjectAnimationUsingKeyFrames>
    
    <!-- Set an attached property using Setter. -->
    <Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
    

    Déclencheurs d’état personnalisés

    Vous pouvez étendre la classe StateTrigger pour créer des déclencheurs personnalisés pour un large éventail de scénarios. Par exemple, vous pouvez créer un StateTrigger pour déclencher différents états en fonction du type d’entrée, puis augmenter les marges autour d’un contrôle lorsque le type d’entrée est tactile. Ou créez un StateTrigger pour appliquer différents états en fonction de la famille d’appareils sur laquelle l’application est exécutée. Pour obtenir des exemples de création de déclencheurs personnalisés et de leur utilisation pour créer des expériences d’interface utilisateur optimisées à partir d’une vue XAML unique, consultez l’exemple de déclencheurs d’état.

    États et styles visuels

    Vous pouvez utiliser des ressources de style dans des états visuels pour appliquer un ensemble de modifications de propriété à plusieurs contrôles. Pour plus d’informations sur l’utilisation de styles, consultez Contrôles de style.

    Dans cet exemple XAML simplifié à partir de l’exemple de déclencheurs d’état, une ressource Style est appliquée à un bouton pour ajuster la taille et les marges de l’entrée tactile ou de la souris. Pour obtenir le code complet et la définition du déclencheur d’état personnalisé, consultez l’exemple de déclencheurs d’état.

    <Page ... >
        <Page.Resources>
            <!-- Styles to be used for mouse vs. touch/pen hit targets -->
            <Style x:Key="MouseStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="5" />
                <Setter Property="Height" Value="20" />
                <Setter Property="Width" Value="20" />
            </Style>
            <Style x:Key="TouchPenStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="15" />
                <Setter Property="Height" Value="40" />
                <Setter Property="Width" Value="40" />
            </Style>
        </Page.Resources>
    
        <RelativePanel>
            <!-- ... -->
            <Button Content="Color Palette Button" x:Name="MenuButton">
                <Button.Flyout>
                    <Flyout Placement="Bottom">
                        <RelativePanel>
                            <Rectangle Name="BlueRect" Fill="Blue"/>
                            <Rectangle Name="GreenRect" Fill="Green" RelativePanel.RightOf="BlueRect" />
                            <!-- ... -->
                        </RelativePanel>
                    </Flyout>
                </Button.Flyout>
            </Button>
            <!-- ... -->
        </RelativePanel>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="InputTypeStates">
                <!-- Second set of VisualStates for building responsive UI optimized for input type.
                     Take a look at InputTypeTrigger.cs class in CustomTriggers folder to see how this is implemented. -->
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- This trigger indicates that this VisualState is to be applied when MenuButton is invoked using a mouse. -->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Mouse" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource MouseStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource MouseStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- Multiple trigger statements can be declared in the following way to imply OR usage.
                             For example, the following statements indicate that this VisualState is to be applied when MenuButton is invoked using Touch OR Pen.-->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Touch" />
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Pen" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Page>