Xamarin.Forms Visual State Manager

Download Sample Scaricare l'esempio

Usa Visual State Manager per apportare modifiche agli elementi XAML in base agli stati di visualizzazione impostati dal codice.

Visual State Manager (VSM) offre un modo strutturato per apportare modifiche visive all'interfaccia utente dal codice. Nella maggior parte dei casi, l'interfaccia utente dell'applicazione viene definita in XAML e questo codice XAML include markup che descrivono in che modo Visual State Manager influisce sugli oggetti visivi dell'interfaccia utente.

Vsm introduce il concetto di stati di visualizzazione. Una Xamarin.Forms visualizzazione, ad esempio, Button può avere diversi aspetti visivi a seconda dello stato sottostante, ovvero se è disabilitata o premuta o ha lo stato attivo per l'input. Questi sono gli stati del pulsante.

Gli stati di visualizzazione vengono raccolti in gruppi di stati di visualizzazione. Tutti gli stati di visualizzazione all'interno di un gruppo di stati di visualizzazione si escludono a vicenda. Sia gli stati di visualizzazione che i gruppi di stati di visualizzazione sono identificati da stringhe di testo semplici.

Xamarin.Forms Visual State Manager definisce un gruppo di stati di visualizzazione denominato "CommonStates" con gli stati di visualizzazione seguenti:

  • "Normal"
  • "Disabilitato"
  • "Incentrato"
  • "Selezionato"

Questo gruppo di stati di visualizzazione è supportato per tutte le classi che derivano da VisualElement, ovvero la classe di base per View e Page.

È anche possibile definire gruppi di stati di visualizzazione e stati di visualizzazione personalizzati, come illustrato in questo articolo.

Nota

Xamarin.Forms Gli sviluppatori che hanno familiarità con i trigger sono consapevoli che i trigger possono anche apportare modifiche agli oggetti visivi nell'interfaccia utente in base alle modifiche apportate alle proprietà di una visualizzazione o alla generazione di eventi. Tuttavia, l'uso di trigger per gestire varie combinazioni di queste modifiche può diventare piuttosto confuso. Storicamente, Visual State Manager è stato introdotto negli ambienti basati su XAML di Windows per alleviare la confusione risultante da combinazioni di stati di visualizzazione. Con VSM, gli stati di visualizzazione all'interno di un gruppo di stati di visualizzazione si escludono sempre a vicenda. In qualsiasi momento, solo uno stato in ogni gruppo è lo stato corrente.

Stati comuni

Visual State Manager consente di includere markup nel file XAML che può modificare l'aspetto visivo di una visualizzazione se la visualizzazione è normale o disabilitata o ha lo stato attivo per l'input. Questi sono noti come stati comuni.

Si supponga, ad esempio, di avere una Entry visualizzazione nella pagina e di voler modificare l'aspetto visivo dell'oggetto Entry nei modi seguenti:

  • L'oggetto Entry deve avere uno sfondo rosa quando l'oggetto Entry è disabilitato.
  • L'oggetto Entry deve avere normalmente uno sfondo lime.
  • L'oggetto Entry deve espandersi fino al doppio dell'altezza normale quando ha lo stato attivo per l'input.

È possibile collegare il markup VSM a una singola visualizzazione oppure definirlo in uno stile se si applica a più visualizzazioni. Le due sezioni successive descrivono questi approcci.

Markup VSM in una visualizzazione

Per associare markup VSM a una Entry visualizzazione, separare prima di tutto i Entry tag iniziale e finale:

<Entry FontSize="18">

</Entry>

Viene assegnata una dimensione esplicita del tipo di carattere perché uno degli stati userà la FontSize proprietà per raddoppiare le dimensioni del testo in Entry.

VisualStateManager.VisualStateGroups Inserire quindi tag tra i tag seguenti:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>

    </VisualStateManager.VisualStateGroups>
</Entry>

VisualStateGroups è una proprietà associabile associata definita dalla VisualStateManager classe . Per altre informazioni sulle proprietà associabili associate, vedere l'articolo Proprietà associate. Questo è il modo in cui la VisualStateGroups proprietà viene associata all'oggetto Entry .

La VisualStateGroups proprietà è di tipo VisualStateGroupList, che è una raccolta di VisualStateGroup oggetti . All'interno dei VisualStateManager.VisualStateGroups tag inserire una coppia di VisualStateGroup tag per ogni gruppo di stati di visualizzazione da includere:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">

        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

Si noti che il VisualStateGroup tag ha un x:Name attributo che indica il nome del gruppo. La VisualStateGroup classe definisce invece una Name proprietà che è possibile usare:

<VisualStateGroup Name="CommonStates">

È possibile usare x:Name o Name ma non entrambi nello stesso elemento.

La VisualStateGroup classe definisce una proprietà denominata States, che è una raccolta di VisualState oggetti . States è la proprietà del contenuto di VisualStateGroups in modo da poter includere i VisualState tag direttamente tra i VisualStateGroup tag. Le proprietà del contenuto sono descritte nell'articolo Sintassi XAML essenziale.

Il passaggio successivo consiste nell'includere una coppia di tag per ogni stato di visualizzazione in tale gruppo. Questi possono essere identificati anche usando x:Name o Name:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">

            </VisualState>

            <VisualState x:Name="Focused">

            </VisualState>

            <VisualState x:Name="Disabled">

            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

VisualState definisce una proprietà denominata Setters, che è una raccolta di Setter oggetti . Si tratta degli stessi Setter oggetti usati in un Style oggetto .

Settersnon è la proprietà content di VisualState, pertanto è necessario includere i tag degli elementi di proprietà per la Setters proprietà :

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">
                <VisualState.Setters>

                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Focused">
                <VisualState.Setters>

                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Disabled">
                <VisualState.Setters>

                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

È ora possibile inserire uno o più Setter oggetti tra ogni coppia di Setters tag. Questi sono gli Setter oggetti che definiscono gli stati di visualizzazione descritti in precedenza:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="Lime" />
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Focused">
                <VisualState.Setters>
                    <Setter Property="FontSize" Value="36" />
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Disabled">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="Pink" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

Ogni Setter tag indica il valore di una determinata proprietà quando lo stato è corrente. Qualsiasi proprietà a cui fa riferimento un Setter oggetto deve essere supportata da una proprietà associabile.

Il markup simile a questo è la base della pagina VSM nella visualizzazione nel programma di esempio VsmDemos . La pagina include tre Entry visualizzazioni, ma solo la seconda include il markup VSM a esso associato:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:VsmDemos"
             x:Class="VsmDemos.MainPage"
             Title="VSM Demos">

    <StackLayout>
        <StackLayout.Resources>
            <Style TargetType="Entry">
                <Setter Property="Margin" Value="20, 0" />
                <Setter Property="FontSize" Value="18" />
            </Style>

            <Style TargetType="Label">
                <Setter Property="Margin" Value="20, 30, 20, 0" />
                <Setter Property="FontSize" Value="Large" />
            </Style>
        </StackLayout.Resources>

        <Label Text="Normal Entry:" />
        <Entry />
        <Label Text="Entry with VSM: " />
        <Entry>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="Lime" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Focused">
                        <VisualState.Setters>
                            <Setter Property="FontSize" Value="36" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Disabled">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="Pink" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>

            <Entry.Triggers>
                <DataTrigger TargetType="Entry"
                             Binding="{Binding Source={x:Reference entry3},
                                               Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Entry.Triggers>
        </Entry>
        <Label Text="Entry to enable 2nd Entry:" />
        <Entry x:Name="entry3"
               Text=""
               Placeholder="Type something to enable 2nd Entry" />
    </StackLayout>
</ContentPage>

Si noti che il secondo Entry include anche un DataTrigger oggetto come parte della relativa Trigger raccolta. In questo modo, l'oggetto Entry deve essere disabilitato fino a quando non viene digitato un elemento nel terzo Entryoggetto . Ecco la pagina all'avvio in esecuzione in iOS, Android e la piattaforma UWP (Universal Windows Platform) (UWP):

VSM on View: Disabled

Lo stato di visualizzazione corrente è "Disabilitato", quindi lo sfondo del secondo Entry è rosa nelle schermate iOS e Android. L'implementazione UWP di non consente di impostare il colore di Entry sfondo quando è Entry disabilitato.

Quando si immette un testo nel terzo Entry, il secondo Entry passa allo stato "Normale" e lo sfondo è ora lime:

VSM on View: Normal

Quando si tocca il secondo Entryoggetto , ottiene lo stato attivo dell'input. Passa allo stato "Attivo" e si espande fino al doppio dell'altezza:

VSM on View: Focused

Si noti che l'oggetto Entry non mantiene lo sfondo lime quando ottiene lo stato attivo per l'input. Quando Visual State Manager passa tra gli stati di visualizzazione, le proprietà impostate dallo stato precedente non vengono impostate. Tenere presente che gli stati di visualizzazione si escludono a vicenda. Lo stato "Normale" non significa esclusivamente che Entry sia abilitato. Significa che Entry è abilitato e non ha lo stato attivo per l'input.

Se si desidera che l'oggetto Entry abbia uno sfondo lime nello stato "Con stato attivo", aggiungere un altro Setter allo stato di visualizzazione:

<VisualState x:Name="Focused">
    <VisualState.Setters>
        <Setter Property="FontSize" Value="36" />
        <Setter Property="BackgroundColor" Value="Lime" />
    </VisualState.Setters>
</VisualState>

Affinché questi Setter oggetti funzionino correttamente, un VisualStateGroup oggetto deve contenere VisualState oggetti per tutti gli stati in tale gruppo. Se è presente uno stato di visualizzazione che non ha oggetti Setter , includerlo comunque come tag vuoto:

<VisualState x:Name="Normal" />

Markup di Visual State Manager in uno stile

Spesso è necessario condividere lo stesso markup di Visual State Manager tra due o più visualizzazioni. In questo caso, si vuole inserire il markup in una Style definizione.

Ecco l'implicito Style esistente per gli Entry elementi nella pagina VSM nella visualizzazione :

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
</Style>

Aggiungere Setter tag per la VisualStateManager.VisualStateGroups proprietà associabile associata:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">

    </Setter>
</Style>

La proprietà content per Setter è Value, quindi il valore della proprietà può essere specificato direttamente all'interno di Value tali tag. Tale proprietà è di tipo VisualStateGroupList:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>

        </VisualStateGroupList>
    </Setter>
</Style>

All'interno di questi tag è possibile includere uno di più VisualStateGroup oggetti:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">

            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

Il resto del markup VSM è identico a quello precedente.

Ecco la pagina VSM in Stile che mostra il markup VSM completo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmInStylePage"
             Title="VSM in Style">
    <StackLayout>
        <StackLayout.Resources>
            <Style TargetType="Entry">
                <Setter Property="Margin" Value="20, 0" />
                <Setter Property="FontSize" Value="18" />
                <Setter Property="VisualStateManager.VisualStateGroups">
                    <VisualStateGroupList>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal">
                                <VisualState.Setters>
                                    <Setter Property="BackgroundColor" Value="Lime" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Focused">
                                <VisualState.Setters>
                                    <Setter Property="FontSize" Value="36" />
                                    <Setter Property="BackgroundColor" Value="Lime" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <VisualState.Setters>
                                    <Setter Property="BackgroundColor" Value="Pink" />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateGroupList>
                </Setter>
            </Style>

            <Style TargetType="Label">
                <Setter Property="Margin" Value="20, 30, 20, 0" />
                <Setter Property="FontSize" Value="Large" />
            </Style>
        </StackLayout.Resources>

        <Label Text="Normal Entry:" />
        <Entry />
        <Label Text="Entry with VSM: " />
        <Entry>
            <Entry.Triggers>
                <DataTrigger TargetType="Entry"
                             Binding="{Binding Source={x:Reference entry3},
                                               Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Entry.Triggers>
        </Entry>
        <Label Text="Entry to enable 2nd Entry:" />
        <Entry x:Name="entry3"
               Text=""
               Placeholder="Type something to enable 2nd Entry" />
    </StackLayout>
</ContentPage>

Ora tutte le Entry visualizzazioni in questa pagina rispondono allo stesso modo degli stati di visualizzazione. Si noti anche che lo stato "Focus" include ora un secondo Setter che fornisce a ogni Entry sfondo lime anche quando ha lo stato attivo per l'input:

VSM in Style

Stati di visualizzazione in Xamarin.Forms

Nella tabella seguente sono elencati gli stati di visualizzazione definiti in Xamarin.Forms:

Classe Stati Ulteriori informazioni
Button Pressed Stati di visualizzazione pulsante
CheckBox IsChecked Stati di visualizzazione CheckBox
CarouselView DefaultItem, CurrentItem, PreviousItemNextItem Stati di visualizzazione CarouselView
ImageButton Pressed Stati di visualizzazione ImageButton
RadioButton Checked, Unchecked Stati di visualizzazione RadioButton
Switch On, Off Cambiare gli stati di visualizzazione
VisualElement Normal, Disabled, FocusedSelected Stati comuni

È possibile accedere a ognuno di questi stati tramite il gruppo di stati di visualizzazione denominato CommonStates.

Inoltre, CollectionView implementa lo Selected stato . Per altre informazioni, vedere Modificare il colore dell'elemento selezionato.

Impostare lo stato su più elementi

Negli esempi precedenti gli stati di visualizzazione sono stati collegati a e gestiti su singoli elementi. Tuttavia, è anche possibile creare stati di visualizzazione collegati a un singolo elemento, ma che impostano proprietà su altri elementi all'interno dello stesso ambito. In questo modo si evita di dover ripetere gli stati di visualizzazione in ogni elemento su cui operano gli stati.

Il Setter tipo ha una TargetName proprietà di tipo string, che rappresenta l'elemento di destinazione che verrà Setter modificato da per uno stato di visualizzazione. Quando la TargetName proprietà è definita, imposta Setter l'oggetto Property dell'elemento definito in TargetName su Value:

<Setter TargetName="label"
        Property="Label.TextColor"
        Value="Red" />

In questo esempio, un Label oggetto denominato label avrà la relativa TextColor proprietà impostata su Red. Quando si imposta la TargetName proprietà è necessario specificare il percorso completo della proprietà in Property. Pertanto, per impostare la TextColor proprietà in un Labeloggetto , Property viene specificato come Label.TextColor.

Nota

Qualsiasi proprietà a cui fa riferimento un Setter oggetto deve essere supportata da una proprietà associabile.

La pagina VSM con Setter TargetName nell'esempio VsmDemos mostra come impostare lo stato su più elementi, da un singolo gruppo di stati di visualizzazione. Il file XAML è costituito da un StackLayout oggetto contenente un Label elemento, un oggetto Entrye un Buttonoggetto :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmSetterTargetNamePage"
             Title="VSM with Setter TargetName">
    <StackLayout Margin="10">
        <Label Text="What is the capital of France?" />
        <Entry x:Name="entry"
               Placeholder="Enter answer" />
        <Button Text="Reveal answer">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
                    <VisualState x:Name="Pressed">
                        <VisualState.Setters>
                            <Setter Property="Scale"
                                    Value="0.8" />
                            <Setter TargetName="entry"
                                    Property="Entry.Text"
                                    Value="Paris" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Button>
    </StackLayout>
</ContentPage>

Il markup VSM è associato a StackLayout. Esistono due stati che si escludono a vicenda, denominati "Normal" e "Pressed", con ogni stato contenente VisualState tag.

Lo stato "Normale" è attivo quando Button non viene premuto e una risposta alla domanda può essere immessa:

VSM Setter TargetName: Normal State

Lo stato "Premuto" diventa attivo quando Button viene premuto:

VSM Setter TargetName: Pressed State

La proprietà "Pressed" VisualState specifica che, quando Button viene premuto, la relativa Scale proprietà verrà modificata dal valore predefinito da 1 a 0,8. Inoltre, la Entry proprietà denominata entry sarà Text impostata su Parigi. Di conseguenza, il risultato è che quando viene premuto viene Button ridimensionato per essere leggermente più piccolo, e le Entry visualizzazioni di Parigi. Quindi, quando viene Button rilasciato, viene ridimensionato in base al valore predefinito 1 e Entry visualizza qualsiasi testo immesso in precedenza.

Importante

I percorsi delle proprietà non sono attualmente supportati negli Setter elementi che specificano la TargetName proprietà .

Definire i propri stati di visualizzazione

Ogni classe che deriva da VisualElement supporta gli stati comuni "Normal", "Focus" e "Disabled". Inoltre, la CollectionView classe supporta lo stato "Selected". Internamente, la VisualElement classe rileva quando diventa abilitata o disabilitata o con stato attivo o non attivo e chiama il metodo statico VisualStateManager.GoToState :

VisualStateManager.GoToState(this, "Focused");

Si tratta dell'unico codice di Visual State Manager disponibile nella VisualElement classe . Poiché GoToState viene chiamato per ogni oggetto basato su ogni classe che deriva da VisualElement, è possibile usare Visual State Manager con qualsiasi VisualElement oggetto per rispondere a queste modifiche.

È interessante notare che il nome del gruppo di stati di visualizzazione "CommonStates" non viene fatto riferimento in modo esplicito in VisualElement. Il nome del gruppo non fa parte dell'API per Visual State Manager. All'interno di uno dei due programmi di esempio mostrati finora, è possibile modificare il nome del gruppo da "CommonStates" a qualsiasi altro elemento e il programma funzionerà ancora. Il nome del gruppo è semplicemente una descrizione generale degli stati in tale gruppo. Si è compreso in modo implicito che gli stati di visualizzazione in qualsiasi gruppo si escludono a vicenda: uno stato e un solo stato è corrente in qualsiasi momento.

Se si vogliono implementare stati di visualizzazione personalizzati, è necessario chiamare VisualStateManager.GoToState dal codice. La maggior parte delle volte si effettua questa chiamata dal file code-behind della classe di pagina.

La pagina Convalida VSM nell'esempio VsmDemos mostra come usare Visual State Manager in connessione con la convalida dell'input. Il file XAML è costituito da un StackLayout oggetto contenente due Label elementi, un Entryoggetto e un Buttonoggetto :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmValidationPage"
             Title="VSM Validation">
    <StackLayout x:Name="stackLayout"
                 Padding="10, 10">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidityStates">
                    <VisualState Name="Valid">
                        <VisualState.Setters>
                            <Setter TargetName="helpLabel"
                                    Property="Label.TextColor"
                                    Value="Transparent" />
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Lime" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Invalid">
                        <VisualState.Setters>
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Pink" />
                            <Setter TargetName="submitButton"
                                    Property="Button.IsEnabled"
                                    Value="False" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        <Label Text="Enter a U.S. phone number:"
               FontSize="Large" />
        <Entry x:Name="entry"
               Placeholder="555-555-5555"
               FontSize="Large"
               Margin="30, 0, 0, 0"
               TextChanged="OnTextChanged" />
        <Label x:Name="helpLabel"
               Text="Phone number must be of the form 555-555-5555, and not begin with a 0 or 1" />
        <Button x:Name="submitButton"
                Text="Submit"
                FontSize="Large"
                Margin="0, 20"
                VerticalOptions="Center"
                HorizontalOptions="Center" />
    </StackLayout>
</ContentPage>

Il markup VSM è associato a StackLayout (denominato stackLayout). Esistono due stati che si escludono a vicenda, denominati "Valid" e "Invalid", con ogni stato contenente VisualState i tag.

Se l'oggetto Entry non contiene un numero di telefono valido, lo stato corrente è "Non valido", quindi ha Entry uno sfondo rosa, il secondo Label è visibile e viene Button disabilitato:

VSM Validation: Invalid State

Quando viene immesso un numero di telefono valido, lo stato corrente diventa "Valido". Ottiene Entry uno sfondo lime, il secondo Label scompare e ora Button è abilitato:

VSM Validation: Valid State

Il file code-behind è responsabile della gestione dell'evento TextChanged da Entry. Il gestore usa un'espressione regolare per determinare se la stringa di input è valida o meno. Il metodo nel file code-behind denominato GoToState chiama il metodo statico VisualStateManager.GoToState per stackLayout:

public partial class VsmValidationPage : ContentPage
{
    public VsmValidationPage()
    {
        InitializeComponent();

        GoToState(false);
    }

    void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        bool isValid = Regex.IsMatch(args.NewTextValue, @"^[2-9]\d{2}-\d{3}-\d{4}$");
        GoToState(isValid);
    }

    void GoToState(bool isValid)
    {
        string visualState = isValid ? "Valid" : "Invalid";
        VisualStateManager.GoToState(stackLayout, visualState);
    }
}

Si noti anche che il GoToState metodo viene chiamato dal costruttore per inizializzare lo stato. Dovrebbe esserci sempre uno stato corrente. Tuttavia, nel codice non è presente alcun riferimento al nome del gruppo di stati di visualizzazione, anche se viene fatto riferimento nel codice XAML come "ValidationStates" a scopo di chiarezza.

Si noti che il file code-behind deve tenere conto solo dell'oggetto nella pagina che definisce gli stati di visualizzazione e di chiamare VisualStateManager.GoToState per questo oggetto. Questo perché entrambi gli stati di visualizzazione hanno come destinazione più oggetti nella pagina.

Ci si potrebbe chiedere: se il file code-behind deve fare riferimento all'oggetto nella pagina che definisce gli stati di visualizzazione, perché il file code-behind non può semplicemente accedere direttamente a questo e ad altri oggetti? Sicuramente potrebbe. Tuttavia, il vantaggio dell'uso di VSM è che è possibile controllare il modo in cui gli elementi visivi reagiscono completamente allo stato in XAML, che mantiene tutta la progettazione dell'interfaccia utente in un'unica posizione. In questo modo si evita di impostare l'aspetto visivo accedendo agli elementi visivi direttamente dal code-behind.

Trigger dello stato di visualizzazione

Gli stati di visualizzazione supportano i trigger di stato, ovvero un gruppo specializzato di trigger che definiscono le condizioni in cui deve essere applicato un oggetto VisualState .

I trigger di stato vengono aggiunti alla raccolta StateTriggers di VisualState. Questa raccolta può contenere un solo o più trigger di stato. VisualState viene applicato quando nella raccolta è attivo un qualsiasi trigger di stato.

Quando si usano trigger di stato per controllare gli stati di visualizzazione, Xamarin.Forms usa le regole di precedenza seguenti per determinare quale trigger (e corrispondente VisualState) sarà attivo:

  1. Qualsiasi trigger che derivi da StateTriggerBase.
  2. AdaptiveTrigger attivato perché la condizione MinWindowWidth è soddisfatta.
  3. AdaptiveTrigger attivato perché la condizione MinWindowHeight è soddisfatta.

Se sono attivi più trigger, ad esempio due trigger personalizzati, contemporaneamente, il primo trigger dichiarato nel markup ha la precedenza.

Per altre informazioni sui trigger di stato, vedere Trigger di stato.

Usare Visual State Manager per il layout adattivo

Un'applicazione Xamarin.Forms in esecuzione su un telefono può in genere essere visualizzata in proporzioni verticale o orizzontale e un Xamarin.Forms programma in esecuzione sul desktop può essere ridimensionato per assumere molte dimensioni e proporzioni diverse. Un'applicazione ben progettata potrebbe visualizzarne il contenuto in modo diverso per questi vari fattori di forma di pagina o finestra.

Questa tecnica è talvolta nota come layout adattivo. Poiché il layout adattivo prevede esclusivamente gli oggetti visivi di un programma, è un'applicazione ideale di Visual State Manager.

Un semplice esempio è un'applicazione che visualizza una piccola raccolta di pulsanti che influiscono sul contenuto dell'applicazione. In modalità verticale, questi pulsanti potrebbero essere visualizzati in una riga orizzontale nella parte superiore della pagina:

VSM Adaptive Layout: Portrait

In modalità orizzontale, la matrice di pulsanti potrebbe essere spostata su un lato e visualizzata in una colonna:

VSM Adaptive Layout: Landscape

Dall'alto verso il basso, il programma è in esecuzione nel piattaforma UWP (Universal Windows Platform), Android e iOS.

La pagina Layout adattivo VSM nell'esempio VsmDemos definisce un gruppo denominato "OrientationStates " con due stati di visualizzazione denominati "Verticale" e "Orizzontale". Un approccio più complesso potrebbe essere basato su diverse larghezze di pagina o finestra.

Il markup VSM si verifica in quattro posizioni nel file XAML. L'oggetto StackLayout denominato mainStack contiene sia il menu che il contenuto, ovvero un Image elemento . Deve StackLayout avere un orientamento verticale in modalità verticale e un orientamento orizzontale in modalità orizzontale:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmAdaptiveLayoutPage"
             Title="VSM Adaptive Layout">

    <StackLayout x:Name="mainStack">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="OrientationStates">
                <VisualState Name="Portrait">
                    <VisualState.Setters>
                        <Setter Property="Orientation" Value="Vertical" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Landscape">
                    <VisualState.Setters>
                        <Setter Property="Orientation" Value="Horizontal" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <ScrollView x:Name="menuScroll">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="OrientationStates">
                    <VisualState Name="Portrait">
                        <VisualState.Setters>
                            <Setter Property="Orientation" Value="Horizontal" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Landscape">
                        <VisualState.Setters>
                            <Setter Property="Orientation" Value="Vertical" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>

            <StackLayout x:Name="menuStack">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup Name="OrientationStates">
                        <VisualState Name="Portrait">
                            <VisualState.Setters>
                                <Setter Property="Orientation" Value="Horizontal" />
                            </VisualState.Setters>
                        </VisualState>
                        <VisualState Name="Landscape">
                            <VisualState.Setters>
                                <Setter Property="Orientation" Value="Vertical" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>

                <StackLayout.Resources>
                    <Style TargetType="Button">
                        <Setter Property="VisualStateManager.VisualStateGroups">
                            <VisualStateGroupList>
                                <VisualStateGroup Name="OrientationStates">
                                    <VisualState Name="Portrait">
                                        <VisualState.Setters>
                                            <Setter Property="HorizontalOptions" Value="CenterAndExpand" />
                                            <Setter Property="Margin" Value="10, 5" />
                                        </VisualState.Setters>
                                    </VisualState>
                                    <VisualState Name="Landscape">
                                        <VisualState.Setters>
                                            <Setter Property="VerticalOptions" Value="CenterAndExpand" />
                                            <Setter Property="HorizontalOptions" Value="Center" />
                                            <Setter Property="Margin" Value="10" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateGroupList>
                        </Setter>
                    </Style>
                </StackLayout.Resources>

                <Button Text="Banana"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="Banana.jpg" />
                <Button Text="Face Palm"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="FacePalm.jpg" />
                <Button Text="Monkey"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="monkey.png" />
                <Button Text="Seated Monkey"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="SeatedMonkey.jpg" />
            </StackLayout>
        </ScrollView>

        <Image x:Name="image"
               VerticalOptions="FillAndExpand"
               HorizontalOptions="FillAndExpand" />
    </StackLayout>
</ContentPage>

Il nome menuScroll interno ScrollView e il StackLayout denominato menuStack implementano il menu dei pulsanti. L'orientamento di questi layout è opposto a mainStack. Il menu deve essere orizzontale in modalità verticale e verticale in modalità orizzontale.

La quarta sezione del markup VSM è in uno stile implicito per i pulsanti stessi. Questo markup imposta VerticalOptionsle proprietà , HorizontalOptionse Margin specifiche per l'orientamento verticale e orizzontale.

Il file code-behind imposta la BindingContext proprietà di menuStack per implementare Button i comandi e associa anche un gestore all'evento SizeChanged della pagina:

public partial class VsmAdaptiveLayoutPage : ContentPage
{
    public VsmAdaptiveLayoutPage ()
    {
        InitializeComponent ();

        SizeChanged += (sender, args) =>
        {
            string visualState = Width > Height ? "Landscape" : "Portrait";
            VisualStateManager.GoToState(mainStack, visualState);
            VisualStateManager.GoToState(menuScroll, visualState);
            VisualStateManager.GoToState(menuStack, visualState);

            foreach (View child in menuStack.Children)
            {
                VisualStateManager.GoToState(child, visualState);
            }
        };

        SelectedCommand = new Command<string>((filename) =>
        {
            image.Source = ImageSource.FromResource("VsmDemos.Images." + filename);
        });

        menuStack.BindingContext = this;
    }

    public ICommand SelectedCommand { private set; get; }
}

Il SizeChanged gestore chiama VisualStateManager.GoToState i due StackLayout elementi e ScrollView e quindi esegue un ciclo tra gli elementi figlio di menuStack per chiamare VisualStateManager.GoToState gli Button elementi.

Può sembrare come se il file code-behind possa gestire le modifiche di orientamento più direttamente impostando le proprietà degli elementi nel file XAML, ma Visual State Manager è sicuramente un approccio più strutturato. Tutti gli oggetti visivi vengono mantenuti nel file XAML, in cui diventano più facili da esaminare, gestire e modificare.

Visual State Manager con Xamarin.University

Xamarin.Forms Video di Visual State Manager 3.0