Layout reattivi con XAML

Il sistema di layout XAML offre il ridimensionamento automatico degli elementi, i pannelli di layout e gli stati di visualizzazione per creare un'interfaccia utente reattiva. Grazie a questo layout reattivo, la tua app verrà visualizzata in modo ottimale su schermi con risoluzioni, densità in pixel, orientamenti e dimensioni delle finestre diversi. Puoi anche usare XAML per riposizionare, ridimensionare, adattare dinamicamente il contenuto, mostrare/nascondere, sostituire o riprogettare l'interfaccia utente della tua app, come illustrato in Tecniche di responsive design. Esamineremo ora come implementare layout reattivi con XAML.

Definire layout con proprietà e pannelli

Le basi di un layout reattivo sono rappresentate dall'uso appropriato di proprietà e pannelli di layout XAML per il riposizionamento, il ridimensionamento e l'adattamento dinamico del contenuto in modo fluido.

Il sistema di layout XAML supporta layout statici e fluidi. In un layout statico si assegnano controlli espliciti pixel e posizioni. Quando l'utente modifica la risoluzione o l'orientamento del dispositivo, l'interfaccia utente non cambia. I layout statici possono essere ritagliati in diversi fattori di forma e dimensioni di visualizzazione. D'altro canto, i layout fluidi riescono a ridursi, ingrandirsi e adattare dinamicamente il contenuto per rispondere allo spazio visivo disponibile in un dispositivo.

In pratica, puoi usare una combinazione di elementi statici e fluidi per creare l'interfaccia utente. Puoi ancora usare elementi e valori statici in alcune posizioni, ma accertati che l'interfaccia utente generale sia reattiva a risoluzioni, dimensioni dello schermo e visualizzazioni diverse.

Vedremo ora come usare le proprietà XAML e i pannelli di layout per creare un layout fluido.

Proprietà di layout

Le proprietà di layout gestiscono le dimensioni e la posizione di un elemento. Per creare un layout fluido, usa il ridimensionamento automatico o proporzionale per gli elementi e consenti ai pannelli di layout di posizionare i relativi elementi figlio in base alle esigenze.

Di seguito vengono esaminate alcune proprietà di layout comuni e viene descritto come utilizzarle per creare layout fluidi.

Altezza e larghezza

Le proprietà Height e Width specificano le dimensioni di un elemento. Puoi impostare valori fissi misurati in pixel effettivi o usare Auto o il ridimensionamento proporzionale.

Il ridimensionamento automatico ridimensiona gli elementi dell'interfaccia utente per adattarli al loro contenuto o al contenitore padre. È anche possibile usare il ridimensionamento automatico con le righe e le colonne di una griglia. Per usare il ridimensionamento automatico, impostare Height e/o Width degli elementi dell'interfaccia utente su Auto.

Nota

Il ridimensionamento di un elemento in base al relativo contenuto o al relativo contenitore dipende da come il contenitore padre gestisce il ridimensionamento degli elementi figlio. Per altre informazioni, vedi Pannelli di layout più avanti in questo articolo.

Il ridimensionamento proporzionale (Star) distribuisce lo spazio disponibile tra le righe e le colonne di una griglia in base a proporzioni ponderate. In XAML i valori delle unità di ridimensionamento proporzionale sono espressi come* (o n* per il ridimensionamento proporzionale Star ponderato). Ad esempio, per specificare che una colonna è cinque volte più larga rispetto alla seconda colonna in un layout a due colonne, usa "5*" e "*" per le proprietà Larghezza negli elementi ColumnDefinition.

Questo esempio combina il ridimensionamento fisso, automatico e proporzionale in una griglia con 4 colonne.

Colonna Dimensionamento Descrizione
Column_1 Auto La colonna verrà ridimensionata per adattarne il contenuto.
Column_2 * Dopo aver calcolato le colonne Auto, la colonna ottiene parte della larghezza rimanente. Column_2 avrà una larghezza pari a metà del Column_4.
Column_3 44 La colonna avrà una larghezza di 44 pixel.
Column_4 2* Dopo aver calcolato le colonne Auto, la colonna ottiene parte della larghezza rimanente. Column_4 sarà il doppio del Column_2.

La larghezza predefinita della colonna è "*", pertanto non è necessario impostare in modo esplicito questo valore per la seconda colonna.

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

Nella finestra di progettazione XAML di Visual Studio il risultato è simile al seguente.

Griglia a 4 colonne nella finestra di progettazione di Visual Studio

Per ottenere le dimensioni di un elemento in fase di esecuzione, usa le proprietà di sola lettura ActualHeight e ActualWidth invece delle proprietà Height e Width.

Vincoli di dimensione

Quando si usa il ridimensionamento automatico nell'interfaccia utente, potrebbe essere comunque necessario inserire vincoli sulle dimensioni di un elemento. Impostare le proprietà MinWidth/MaxWidth e MinHeight/MaxHeight specificano i valori che vincolano le dimensioni di un elemento, consentendo il ridimensionamento fluido.

In un oggetto Grid è possibile usare MinWidth/MaxWidth anche con le definizioni di colonna e MinHeight/MaxHeight con le definizioni di riga.

Allineamento

Usare le proprietà HorizontalAlignment e VerticalAlignment specificano come deve essere posizionato un elemento all'interno del relativo contenitore padre.

  • I valori per HorizontalAlignment sono Left, Center, Right e Stretch.
  • I valori per VerticalAlignment sono Top, Center, Bottom e Stretch.

Con l'allineamento Allunga , gli elementi riempiono tutto lo spazio fornito nel contenitore principale. Stretch è l'impostazione predefinita per entrambe le proprietà di allineamento. Tuttavia, alcuni controlli, ad esempio Button, eseguono l'override di questo valore nello stile predefinito. Qualsiasi elemento che può avere elementi figlio può trattare il valore Stretch per le proprietà HorizontalAlignment e VerticalAlignment in modo univoco. Ad esempio, un elemento che usa i valori predefiniti Stretch posizionati in una griglia si estende per riempire la cella che lo contiene. Lo stesso elemento posizionato in un oggetto Canvas viene ridimensionato in base al relativo contenuto. Per altre info su come ogni pannello gestisce il valore Stretch, vedi l'articolo Pannelli di layout.

Per altre info, vedi l'articolo di allineamento, margine e spaziatura interna e le pagine di riferimento HorizontalAlignment e VerticalAlignment.

Visibilità

Puoi mostrare o nascondere un elemento impostandone la proprietà Visibility su uno dei valori dell'enumerazione Visibility: Visible o Collapsed. Quando un elemento è Collapsed, non occupa spazio nel layout dell'interfaccia utente.

È possibile modificare la proprietà Visibility di un elemento nel codice o in uno stato di visualizzazione. Quando viene modificata la proprietà Visibility di un elemento, vengono modificati anche tutti i relativi elementi figlio. È possibile sostituire le sezioni dell'interfaccia utente rivelando un pannello mentre si comprime un altro.

Suggerimento

Se hai elementi dell'interfaccia utente impostati in modo predefinito su Collapsed, gli oggetti vengono ancora creati, ma non visualizzati, all'avvio. È possibile posticipare il caricamento di questi elementi fino a quando non vengono visualizzati usando l'attributo x:Load per ritardare la creazione degli oggetti. Ciò può migliorare le prestazioni di avvio. Per altre info, vedi attributo x:Load.

Risorse di stile

Non è necessario impostare ogni valore di proprietà singolarmente in un controllo. In genere è più efficiente raggruppare i valori delle proprietà in una risorsa Style e applicare style a un controllo . Ciò vale soprattutto quando è necessario applicare gli stessi valori di proprietà a molti controlli. Per altre informazioni su come utilizzare lo stile, vedere Controlli di stile.

Pannelli di layout

Per posizionare gli oggetti visivi, è necessario inserirli in un pannello o in un altro oggetto contenitore. Il framework XAML fornisce varie classi di pannelli, ad esempio Canvas, Grid, RelativePanel e StackPanel, che fungono da contenitori e consentono di posizionare e disporre gli elementi dell'interfaccia utente all'interno di essi.

La cosa principale da considerare quando si sceglie un pannello di layout è il modo in cui il pannello posiziona e ridimensiona gli elementi figlio. Potrebbe anche essere necessario considerare il modo in cui gli elementi figlio sovrapposti vengono sovrapposti tra loro.

Ecco un confronto tra le funzionalità principali dei controlli pannello forniti nel framework XAML.

Controllo Panel Descrizione
Canvas Canvas non supporta l'interfaccia utente fluida. Puoi controllare tutti gli aspetti del posizionamento e del ridimensionamento degli elementi figlio. In genere viene usato per casi speciali come la creazione di grafica o per definire piccole aree statiche di un'interfaccia utente adattiva più grande. È possibile usare il codice o gli stati di visualizzazione per riposizionare gli elementi in fase di esecuzione.
  • Gli elementi vengono posizionati in modo assoluto usando le proprietà associate Canvas.Top e Canvas.Left.
  • È possibile specificare in modo esplicito il layering usando la proprietà associata Canvas.ZIndex.
  • I valori stretch per HorizontalAlignment/VerticalAlignment vengono ignorati. Se le dimensioni di un elemento non vengono impostate in modo esplicito, le dimensioni vengono ridimensionate sul relativo contenuto.
  • Il contenuto figlio non viene ritagliato visivamente se è più grande del pannello.
  • Il contenuto figlio non è vincolato dai limiti del pannello.
  • Griglia Grid supporta il ridimensionamento fluido degli elementi figlio. È possibile usare il codice o gli stati di visualizzazione per riposizionare e rielaborare gli elementi.
  • Gli elementi vengono disposti in righe e colonne usando le proprietà associate Grid.Row e Grid.Column.
  • Gli elementi possono estendersi su più righe e colonne usando le proprietà associate Grid.RowSpan e Grid.ColumnSpan.
  • I valori stretch per HorizontalAlignment/VerticalAlignment vengono rispettati. Se le dimensioni di un elemento non sono impostate in modo esplicito, si estende per riempire lo spazio disponibile nella cella della griglia.
  • Il contenuto figlio viene ritagliato visivamente se è più grande del pannello.
  • Le dimensioni del contenuto sono vincolate dai limiti del pannello, quindi il contenuto scorrevole mostra le barre di scorrimento, se necessario.
  • RelativePanel
  • Gli elementi vengono disposti in relazione al bordo o al centro del pannello e in relazione tra loro.
  • Gli elementi vengono posizionati usando un'ampia gamma di proprietà associate che consentono l'allineamento del pannello di controllo, l'allineamento di pari livello e la posizione di pari livello.
  • I valori stretch per HorizontalAlignment/VerticalAlignment vengono ignorati a meno che le proprietà associate relativePanel per l'allineamento non causano l'estensione (ad esempio, un elemento è allineato ai bordi destro e sinistro del pannello). Se le dimensioni di un elemento non vengono impostate in modo esplicito e non vengono estese, le dimensioni vengono ridimensionate sul relativo contenuto.
  • Il contenuto figlio viene ritagliato visivamente se è più grande del pannello.
  • Le dimensioni del contenuto sono vincolate dai limiti del pannello, quindi il contenuto scorrevole mostra le barre di scorrimento, se necessario.
  • StackPanel
  • Gli elementi vengono impilati in una singola riga verticalmente o orizzontalmente.
  • I valori stretch per HorizontalAlignment/VerticalAlignment vengono rispettati nella direzione opposta alla proprietà Orientation. Se le dimensioni di un elemento non sono impostate in modo esplicito, si estende per riempire la larghezza disponibile (o l'altezza se Orientation è Horizontal). Nella direzione specificata dalla proprietà Orientation, un elemento viene ridimensionato per il relativo contenuto.
  • Il contenuto figlio viene ritagliato visivamente se è più grande del pannello.
  • Le dimensioni del contenuto non sono vincolate dai limiti del pannello nella direzione specificata dalla proprietà Orientation, quindi il contenuto scorrevole si estende oltre i limiti del pannello e non visualizza le barre di scorrimento. È necessario vincolare in modo esplicito l'altezza (o la larghezza) del contenuto figlio per visualizzarne le barre di scorrimento.
  • VariableSizedWrapGrid
  • Gli elementi vengono disposti in righe o colonne che eseguono automaticamente il wrapping in una nuova riga o colonna quando viene raggiunto il valore MaximumRowsOrColumns.
  • Indica se gli elementi sono disposti in righe o colonne viene specificato dalla proprietà Orientation.
  • Gli elementi possono estendersi su più righe e colonne usando le proprietà associate VariableSizedWrapGrid.RowSpan e VariableSizedWrapGrid.ColumnSpan.
  • I valori stretch per HorizontalAlignment e VerticalAlignment vengono ignorati. Gli elementi vengono ridimensionati in base alle proprietà ItemHeight e ItemWidth. Se queste proprietà non sono impostate, accettano i valori dalla dimensione della prima cella.
  • Il contenuto figlio viene ritagliato visivamente se è più grande del pannello.
  • Le dimensioni del contenuto sono vincolate dai limiti del pannello, quindi il contenuto scorrevole mostra le barre di scorrimento, se necessario.
  • Per informazioni dettagliate ed esempi di questi pannelli, vedere Pannelli di layout.

    I pannelli di layout consentono di organizzare l'interfaccia utente in gruppi logici di controlli. Quando li usi con le impostazioni di proprietà appropriate, ottieni il supporto per il ridimensionamento automatico, il riposizionamento e il riflow degli elementi dell'interfaccia utente. Tuttavia, la maggior parte dei layout dell'interfaccia utente richiede ulteriori modifiche quando sono presenti modifiche significative alle dimensioni della finestra. A questo scopo, è possibile usare gli stati di visualizzazione.

    Layout adattivi con stati di visualizzazione e trigger di stato

    Usa gli stati di visualizzazione per apportare modifiche significative all'interfaccia utente in base alle dimensioni della finestra o ad altre modifiche.

    Quando la finestra dell'app aumenta o si riduce oltre una determinata quantità, è possibile modificare le proprietà del layout per riposizionare, ridimensionare, riflow, rivelare o sostituire sezioni dell'interfaccia utente. È possibile definire diversi stati di visualizzazione per l'interfaccia utente e applicarli quando la larghezza della finestra o l'altezza della finestra supera una soglia specificata.

    VisualState definisce i valori delle proprietà applicati a un elemento quando si trova in uno stato specifico. È possibile raggruppare gli stati di visualizzazione in un oggetto VisualStateManager che applica l'oggetto VisualState appropriato quando vengono soddisfatte le condizioni specificate. Un oggetto AdaptiveTrigger rappresenta un modo semplice per impostare la soglia (denominata anche 'punto di interruzione'), superata la quale viene applicato uno stato in XAML. Per applicare uno stato di visualizzazione, è possibile chiamare il metodo VisualStateManager.GoToState nel codice. Nelle sezioni successive sono riportati alcuni esempi di entrambi i modi.

    Impostare gli stati di visualizzazione nel codice

    Per applicare uno stato di visualizzazione, è possibile chiamare il metodo VisualStateManager.GoToState nel codice. Ad esempio, per applicare uno stato quando la finestra dell'app è una dimensione specifica, gestire l'evento SizeChanged e chiamare GoToState per applicare lo stato appropriato.

    In questo caso, VisualStateGroup contiene due definizioni di VisualState. Il primo, DefaultState, è vuoto. Quando viene applicata, vengono applicati i valori definiti nella pagina XAML. Il secondo, WideState,modifica la proprietà DisplayMode di SplitView in Inline e apre il riquadro. Questo stato viene applicato al gestore dell'evento SizeChanged se la larghezza della finestra è maggiore di 640 pixel effettivi.

    Nota

    Windows non fornisce all'app un sistema per rilevare lo specifico dispositivo in cui è in esecuzione. Può indicare la famiglia di dispositivi (desktop e così via) in cui l'app è in esecuzione, la risoluzione effettiva e la quantità di spazio sullo schermo disponibile per l'app (le dimensioni della finestra dell'app). È consigliabile definire gli stati di visualizzazione per dimensioni dello schermo e punti di interruzione.

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

    Impostare gli stati di visualizzazione nel markup XAML

    Prima di Windows 10, le definizioni di VisualState richiedevano oggetti Storyboard per le modifiche alle proprietà e dovevi chiamare GoToState nel codice per applicare lo stato. come illustrato nell'esempio precedente. Verranno comunque visualizzati molti esempi che usano questa sintassi oppure si potrebbe avere codice esistente che lo usa.

    A partire da Windows 10, puoi usare la sintassi semplificata Setter illustrata qui e puoi usare StateTrigger nel markup XAML per applicare lo stato. Si usano trigger di stato per creare regole semplici che attivano automaticamente modifiche dello stato di visualizzazione in risposta a un evento dell'app.

    Questo esempio esegue la stessa operazione dell'esempio precedente, ma usa la sintassi Setter semplificata anziché uno Storyboard per definire le modifiche delle proprietà. Invece di chiamare GoToState, usa il trigger di stato AdaptiveTrigger predefinito per applicare lo stato. Quando si usano trigger di stato, non è necessario definire un oggetto vuoto DefaultState. Le impostazioni predefinite vengono riapplicate automaticamente quando le condizioni del trigger di stato non vengono più soddisfatte.

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

    Importante

    Nell'esempio precedente la proprietà associata VisualStateManager.VisualStateGroups viene impostata sull'elemento Grid. Quando si usa StateTriggers, assicurarsi sempre che VisualStateGroups sia associato al primo elemento figlio della radice in modo che i trigger abbiano effetto automaticamente. (In questo caso, Grid è il primo elemento figlio dell’elemento radice Page.)

    Sintassi delle proprietà associate

    In VisualState, in genere si imposta un valore per una proprietà del controllo o per una delle proprietà associate del pannello che contiene il controllo . Quando si imposta una proprietà associata, usare le parentesi intorno al nome della proprietà associata.

    Questo esempio mostra come impostare la proprietà associata RelativePanel.AlignHorizontalCenterWithPanel in una proprietà TextBox denominata myTextBox. Il primo XAML usa la sintassi ObjectAnimationUsingKeyFrames e la seconda usa la sintassi 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"/>
    

    Trigger di stato personalizzati

    È possibile estendere la classe StateTrigger per creare trigger personalizzati per un'ampia gamma di scenari. Ad esempio, è possibile creare uno StateTrigger per attivare stati diversi in base al tipo di input, quindi aumentare i margini intorno a un controllo quando il tipo di input è tocco. In alternativa, creare uno StateTrigger per applicare stati diversi in base alla famiglia di dispositivi in cui viene eseguita l'app. Per esempi di come creare trigger personalizzati e usarli per creare esperienze di interfaccia utente ottimizzate da una singola visualizzazione XAML, vedi l'esempio di trigger di stato.

    Stati e stili di visualizzazione

    È possibile usare le risorse style negli stati di visualizzazione per applicare un set di modifiche alle proprietà a più controlli. Per altre informazioni su come utilizzare lo stile, vedere Controlli di stile.

    In questo codice XAML semplificato dell'esempio trigger di stato, una risorsa Style viene applicata a un pulsante per regolare le dimensioni e i margini per l'input tocco o del mouse. Per il codice completo e la definizione del trigger di stato personalizzato, vedere l'esempio di trigger di stato.

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