Desencadenadores de Xamarin.Forms

Los desencadenadores permiten expresar acciones de forma declarativa en XAML que cambian la apariencia de controles en función de eventos o cambios en propiedades. Además, los desencadenadores de estado, que son un grupo especializado de desencadenadores, definen cuándo se debe aplicar la clase VisualState.

Puede asignar un desencadenador directamente a un control o agregarlo a un diccionario de recursos de nivel de aplicación o página que se vaya a aplicar a varios controles.

Desencadenadores de propiedad

Un desencadenador simple se puede expresar puramente en XAML, mediante la incorporación de un elemento Trigger a la colección de desencadenadores de un control. En este ejemplo se muestra un desencadenador que cambia un color de fondo Entry cuando recibe el foco:

<Entry Placeholder="enter name">
    <Entry.Triggers>
        <Trigger TargetType="Entry"
                 Property="IsFocused" Value="True">
            <Setter Property="BackgroundColor" Value="Yellow" />
            <!-- multiple Setters elements are allowed -->
        </Trigger>
    </Entry.Triggers>
</Entry>

Las partes importantes de la declaración del desencadenador son:

  • TargetType: tipo de control al que se aplica el desencadenador.

  • Property: propiedad en el control que se supervisa.

  • Value: valor, cuando se produce para la propiedad supervisada, que hace que el desencadenador se active.

  • Setter: colección de elementos Setter que se puede agregar cuando se cumple la condición del desencadenador. Debe especificar los elementos Property y Value que se van a establecer.

  • EnterActions y ExitActions (no mostrados): se escriben en código y se pueden usar además de los elementos Setter (o en su lugar). Se describen abajo.

Aplicar un desencadenador mediante un estilo

Los desencadenadores también se pueden agregar a una declaración Style en un control, en una página o una aplicación ResourceDictionary. Este ejemplo declara un estilo implícito (es decir, no se establece Key), lo que significa que se aplica a todos los controles Entry en la página.

<ContentPage.Resources>
    <ResourceDictionary>
        <Style TargetType="Entry">
                        <Style.Triggers>
                <Trigger TargetType="Entry"
                         Property="IsFocused" Value="True">
                    <Setter Property="BackgroundColor" Value="Yellow" />
                    <!-- multiple Setters elements are allowed -->
                </Trigger>
            </Style.Triggers>
        </Style>
    </ResourceDictionary>
</ContentPage.Resources>

Desencadenadores de datos

Los desencadenadores de datos usan enlaces de datos para supervisar otro control a fin de que se llame a los elementos Setter. En lugar del atributo Property de un desencadenador de propiedad, establezca el atributo Binding para supervisar el valor especificado.

El ejemplo siguiente usa la sintaxis de enlace de datos ,{Binding Source={x:Reference entry}, Path=Text.Length} que es como se hace referencia a las propiedades de otro control. Cuando la longitud de entry es cero, el desencadenador se activa. En este ejemplo el desencadenador deshabilita el botón cuando la entrada está vacía.

<!-- the x:Name is referenced below in DataTrigger-->
<!-- tip: make sure to set the Text="" (or some other default) -->
<Entry x:Name="entry"
       Text=""
       Placeholder="required field" />

<Button x:Name="button" Text="Save"
        FontSize="Large"
        HorizontalOptions="Center">
    <Button.Triggers>
        <DataTrigger TargetType="Button"
                     Binding="{Binding Source={x:Reference entry},
                                       Path=Text.Length}"
                     Value="0">
            <Setter Property="IsEnabled" Value="False" />
            <!-- multiple Setters elements are allowed -->
        </DataTrigger>
    </Button.Triggers>
</Button>

Sugerencia

Al evaluar Path=Text.Length proporciona siempre un valor predeterminado para la propiedad de destino (por ejemplo, Text="") porque de lo contrario será null y el desencadenador no funcionará como esperas.

Además de especificar los elementos Setter, también puede proporcionar EnterActions y ExitActions.

Desencadenadores de eventos

El elemento EventTrigger solo requiere una propiedad Event, como "Clicked", en el ejemplo siguiente.

<EventTrigger Event="Clicked">
    <local:NumericValidationTriggerAction />
</EventTrigger>

Tenga en cuenta que no hay elementos Setter, sino una referencia a una clase definida por local:NumericValidationTriggerAction que requiere que se declare xmlns:local en el código XAML de la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:WorkingWithTriggers;assembly=WorkingWithTriggers"

La propia clase implementa TriggerAction, lo que significa que debe proporcionar una invalidación para el método Invoke al que se llama cada vez que se produce el evento desencadenador.

Una implementación de acción de desencadenador debe:

  • Implementar la clase genérica TriggerAction<T>, con el parámetro genérico correspondiente al tipo de control al que se va a aplicar el desencadenador. Puede usar superclases como VisualElement para escribir acciones de desencadenador que funcionen con una serie de controles, o especificar un tipo de control como Entry.

  • Invalidar el método Invoke: se llama a este método cada vez que se cumplen los criterios del desencadenador.

  • Opcionalmente, exponer propiedades que se pueden establecer en el código XAML cuando se declara el desencadenador. Para obtener un ejemplo de esto, vea la clase VisualElementPopTriggerAction en la aplicación de ejemplo que lo acompaña.

public class NumericValidationTriggerAction : TriggerAction<Entry>
{
    protected override void Invoke (Entry entry)
    {
        double result;
        bool isValid = Double.TryParse (entry.Text, out result);
        entry.TextColor = isValid ? Color.Default : Color.Red;
    }
}

Después, el desencadenador de eventos se puede usar desde XAML:

<EventTrigger Event="TextChanged">
    <local:NumericValidationTriggerAction />
</EventTrigger>

Tenga cuidado al compartir desencadenadores en una instancia de ResourceDictionary, ya que una instancia se comparte entre controles, con lo que cualquier estado que se configure una vez se va a aplicar a todos ellos.

Tenga en cuenta que los desencadenadores de eventos no admiten los elementos EnterActions y ExitActionsdescritos abajo.

Multi-desencadenadores

Un elemento MultiTrigger se parece Trigger o DataTrigger, salvo que en él puede haber más de una condición. Todas las condiciones deben cumplirse para que se desencadenen los elementos Setter.

Este es el ejemplo de un desencadenador de un botón que se enlaza a dos entradas diferentes (email y phone):

<MultiTrigger TargetType="Button">
    <MultiTrigger.Conditions>
        <BindingCondition Binding="{Binding Source={x:Reference email},
                                   Path=Text.Length}"
                               Value="0" />
        <BindingCondition Binding="{Binding Source={x:Reference phone},
                                   Path=Text.Length}"
                               Value="0" />
    </MultiTrigger.Conditions>
    <Setter Property="IsEnabled" Value="False" />
    <!-- multiple Setter elements are allowed -->
</MultiTrigger>

La colección Conditions también puede contener elementos PropertyCondition como este:

<PropertyCondition Property="Text" Value="OK" />

Compilar un multi-desencadenador "require all"

El multi-desencadenador solo actualiza su control cuando se cumplen todas las condiciones. Las pruebas con "todas las longitudes de campo son cero" (como una página de inicio de sesión donde deben completarse todas las entradas) son complicadas, porque se quiere una condición "where Text.Length > 0", pero esto no se puede expresar en XAML.

Se puede hacer con un elemento IValueConverter. El código convertidor siguiente transforma el enlace Text.Length en un bool que indica si un campo está vacío o no:

public class MultiTriggerConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        if ((int)value > 0) // length > 0 ?
            return true;            // some data has been entered
        else
            return false;            // input is empty
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        throw new NotSupportedException ();
    }
}

Para usar este convertidor en un multi-desencadenador, primero agréguelo al diccionario de recursos de la página (junto con una definición de espacio de nombres personalizada xmlns:local):

<ResourceDictionary>
   <local:MultiTriggerConverter x:Key="dataHasBeenEntered" />
</ResourceDictionary>

A continuación se muestra el código XAML. Observe las siguientes diferencias con respecto al primer ejemplo de multi-desencadenador:

  • El botón tiene IsEnabled="false" establecido de forma predeterminada.
  • Las condiciones del multi-desencadenador usan el convertidor para convertir el valor Text.Length en un boolean.
  • Cuando todas las condiciones son true, el establecedor convierte en true la propiedad IsEnabled del botón.
<Entry x:Name="user" Text="" Placeholder="user name" />

<Entry x:Name="pwd" Text="" Placeholder="password" />

<Button x:Name="loginButton" Text="Login"
        FontSize="Large"
        HorizontalOptions="Center"
        IsEnabled="false">
  <Button.Triggers>
    <MultiTrigger TargetType="Button">
      <MultiTrigger.Conditions>
        <BindingCondition Binding="{Binding Source={x:Reference user},
                              Path=Text.Length,
                              Converter={StaticResource dataHasBeenEntered}}"
                          Value="true" />
        <BindingCondition Binding="{Binding Source={x:Reference pwd},
                              Path=Text.Length,
                              Converter={StaticResource dataHasBeenEntered}}"
                          Value="true" />
      </MultiTrigger.Conditions>
      <Setter Property="IsEnabled" Value="True" />
    </MultiTrigger>
  </Button.Triggers>
</Button>

Estas capturas de pantalla muestran la diferencia entre los dos ejemplos de multi-desencadenadores anteriores. En la parte superior de las pantallas, la entrada de texto con un solo elemento Entry basta para habilitar el botón Guardar. En la parte inferior de las pantallas, el botón Iniciar sesión permanece inactivo hasta que ambos campos contienen datos.

Ejemplos de desencadenadores múltiples

EnterActions y ExitActions

Otra forma de implementar cambios cuando se produce un desencadenador es mediante la incorporación de colecciones EnterActions y ExitActions y la especificación de implementaciones TriggerAction<T>.

La colección EnterActions se usa para definir un elemento IList de objetos TriggerAction que se invocará cuando se cumpla la condición del desencadenador. La colección ExitActions se usa para definir un elemento IList de objetos TriggerAction que se invocará cuando ya no se cumpla la condición del desencadenador.

Nota:

La clase EventTrigger omite los objetos TriggerAction definidos en las colecciones EnterActions y ExitActions.

Puede proporcionar tanto EnterActions como ExitActions, así como Setter en un desencadenador, pero tenga en cuenta que se llama a los elementos Setter de inmediato (no se espera a que se completen EnterAction o ExitAction). También puede hacer todo en el código y no usar elementos Setter en absoluto.

<Entry Placeholder="enter job title">
    <Entry.Triggers>
        <Trigger TargetType="Entry"
                 Property="Entry.IsFocused" Value="True">
            <Trigger.EnterActions>
                <local:FadeTriggerAction StartsFrom="0" />
            </Trigger.EnterActions>

            <Trigger.ExitActions>
                <local:FadeTriggerAction StartsFrom="1" />
            </Trigger.ExitActions>
            <!-- You can use both Enter/Exit and Setter together if required -->
        </Trigger>
    </Entry.Triggers>
</Entry>

Como siempre, cuando se hace referencia a una clase en XAML, se debe declarar un espacio de nombres como xmlns:local, como se muestra aquí:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:WorkingWithTriggers;assembly=WorkingWithTriggers"

El código FadeTriggerAction se muestra a continuación:

public class FadeTriggerAction : TriggerAction<VisualElement>
{
    public int StartsFrom { set; get; }

    protected override void Invoke(VisualElement sender)
    {
        sender.Animate("FadeTriggerAction", new Animation((d) =>
        {
            var val = StartsFrom == 1 ? d : 1 - d;
            // so i was aiming for a different color, but then i liked the pink :)
            sender.BackgroundColor = Color.FromRgb(1, val, 1);
        }),
        length: 1000, // milliseconds
        easing: Easing.Linear);
    }
}

Desencadenadores de estado

Los desencadenadores de estado son un conjunto especializado de desencadenadores que definen las condiciones en las que se debe aplicar VisualState.

Los desencadenadores de estado se agregan a la colección StateTriggers de una clase VisualState. Esta colección puede contener un único desencadenador de estado o varios desencadenadores de estado. Se aplicará una clase VisualState cuando cualquier desencadenador de estado de la colección esté activo.

Al usar desencadenadores de estado para controlar los estados visuales, Xamarin.Forms usa las siguientes reglas de prioridad para determinar qué desencadenador, y la clase VisualState correspondiente, se activará:

  1. Cualquier desencadenador derivado de StateTriggerBase.
  2. Una clase AdaptiveTrigger activada debido a que se cumple la condición MinWindowWidth.
  3. Una clase AdaptiveTrigger activada debido a que se cumple la condición MinWindowHeight.

Si hay varios desencadenadores activos simultáneamente (por ejemplo, dos desencadenadores personalizados), tiene prioridad el primer desencadenador declarado en el marcado.

Nota:

Los desencadenadores de estado se pueden establecer en una clase Style o directamente en los elementos.

Para obtener más información sobre los estados visuales, vea Administrador de estado visual de Xamarin.Forms.

Desencadenador de estado

La clase StateTrigger, que se deriva de la clase StateTriggerBase, tiene una propiedad enlazable IsActive. Un elemento StateTrigger desencadena un cambio de VisualState cuando cambia el valor de la propiedad IsActive.

La clase StateTriggerBase, que es la clase base de todos los desencadenadores de estado, tiene una propiedad IsActive y un evento IsActiveChanged. Este evento se desencadena cuando se produce un cambio de VisualState. Además, la clase StateTriggerBase tiene métodos OnAttached y OnDetached que se pueden invalidar.

Importante

La propiedad enlazable StateTrigger.IsActive oculta la propiedad StateTriggerBase.IsActive heredada.

En el siguiente ejemplo de XAML, se muestra una clase Style que incluye objetos StateTrigger:

<Style TargetType="Grid">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup>
                <VisualState x:Name="Checked">
                    <VisualState.StateTriggers>
                        <StateTrigger IsActive="{Binding IsToggled}"
                                      IsActiveChanged="OnCheckedStateIsActiveChanged" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="Black" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Unchecked">
                    <VisualState.StateTriggers>
                        <StateTrigger IsActive="{Binding IsToggled, Converter={StaticResource inverseBooleanConverter}}"
                                      IsActiveChanged="OnUncheckedStateIsActiveChanged" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="White" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

En este ejemplo, la clase Style implícita se destina a objetos Grid. Cuando la propiedad IsToggled del objeto enlazado es true, el color de fondo de Grid se establece en negro. Cuando la propiedad IsToggled del objeto enlazado se convierte en false, se desencadena un cambio de VisualState, y el color de fondo de Grid cambia a blanco.

Además, cada vez que cambia una clase VisualState, se desencadena el evento IsActiveChanged para el elemento VisualState. Cada VisualState registra un controlador de eventos para este evento:

void OnCheckedStateIsActiveChanged(object sender, EventArgs e)
{
    StateTriggerBase stateTrigger = sender as StateTriggerBase;
    Console.WriteLine($"Checked state active: {stateTrigger.IsActive}");
}

void OnUncheckedStateIsActiveChanged(object sender, EventArgs e)
{
    StateTriggerBase stateTrigger = sender as StateTriggerBase;
    Console.WriteLine($"Unchecked state active: {stateTrigger.IsActive}");
}

En este ejemplo, cuando se desencadena un controlador para el evento IsActiveChanged, el controlador indica si la clase VisualState está activa o no. Por ejemplo, los mensajes siguientes aparecen en la ventana de la consola cuando cambian del estado visual Checked al estado visual Unchecked:

Checked state active: False
Unchecked state active: True

Nota:

Para crear desencadenadores de estado personalizados, derive de la clase StateTriggerBase e invalide los métodos OnAttached y OnDetached para realizar cualquier registro y limpieza necesarios.

Desencadenador adaptable

Una clase AdaptiveTrigger desencadena un cambio de VisualState cuando la ventana tiene un alto o un ancho especificado. Este desencadenador tiene dos propiedades enlazables:

Nota:

El elemento AdaptiveTrigger deriva de la clase StateTriggerBase y, por tanto, puede adjuntar un controlador de eventos al evento IsActiveChanged.

En el siguiente ejemplo de XAML, se muestra una clase Style que incluye objetos AdaptiveTrigger:

<Style TargetType="StackLayout">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup>
                <VisualState x:Name="Vertical">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="Orientation"
                                Value="Vertical" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Horizontal">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="800" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="Orientation"
                                Value="Horizontal" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

En este ejemplo, la clase Style implícita se destina a objetos StackLayout. Cuando el ancho de la ventana tiene entre 0 y 800 unidades independientes del dispositivo, los objetos StackLayout a los que se aplica la clase Style tendrán una orientación vertical. Cuando el ancho de la ventana es >= 800 unidades independientes del dispositivo, se desencadena el cambio de VisualState y la orientación del StackLayout cambia a horizontal:

StackLayout VisualState VerticalStackLayout VisualState Horizontal

Las propiedades MinWindowHeight y MinWindowWidth se pueden utilizar de forma independiente o combinándolas entre sí. En el siguiente ejemplo de XAML se muestra la configuración de ambas propiedades:

<AdaptiveTrigger MinWindowWidth="800"
                 MinWindowHeight="1200"/>

En este ejemplo, AdaptiveTrigger indica que VisualState correspondiente se aplicará cuando el ancho de la ventana actual es >= 800 unidades independientes del dispositivo y el alto de la ventana actual es >= 1200 unidades independientes del dispositivo.

Comparación del desencadenador de estado

CompareStateTrigger desencadena un cambio de VisualState cuando una propiedad es igual a un valor específico. Este desencadenador tiene dos propiedades enlazables:

  • Property, de tipo object, que indica la propiedad comparada por el desencadenador.
  • Value, de tipo object, que indica el valor al que debe aplicarse VisualState.

Nota:

El elemento CompareStateTrigger deriva de la clase StateTriggerBase y, por tanto, puede adjuntar un controlador de eventos al evento IsActiveChanged.

En el siguiente ejemplo de XAML, se muestra una clase Style que incluye objetos CompareStateTrigger:

<Style TargetType="Grid">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup>
                <VisualState x:Name="Checked">
                    <VisualState.StateTriggers>
                        <CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
                                             Value="True" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="Black" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Unchecked">
                    <VisualState.StateTriggers>
                        <CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
                                             Value="False" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="White" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>
...
<Grid>
    <Frame BackgroundColor="White"
           CornerRadius="12"
           Margin="24"
           HorizontalOptions="Center"
           VerticalOptions="Center">
        <StackLayout Orientation="Horizontal">
            <CheckBox x:Name="checkBox"
                      VerticalOptions="Center" />
            <Label Text="Check the CheckBox to modify the Grid background color."
                   VerticalOptions="Center" />
        </StackLayout>
    </Frame>
</Grid>

En este ejemplo, la clase Style implícita se destina a objetos Grid. Cuando la propiedad IsChecked de CheckBox es false, el color de fondo de Grid se establece en blanco. Cuando la propiedad CheckBox.IsChecked se convierte en true, se desencadena un cambio de VisualState, y el color de fondo de Grid cambia a negro:

Captura de pantalla de un cambio de estado visual desencadenado, en iOS y Android, con desencadenador desactivado.Captura de pantalla de un cambio de estado visual desencadenado, en iOS y Android, con desencadenador activado.

Desencadenador de estado de dispositivos

DeviceStateTrigger desencadena un cambio de VisualState basado en la plataforma del dispositivo en que se ejecuta la aplicación. Este desencadenador tiene una única propiedad enlazable:

  • Device, de tipo string, que indica la plataforma del dispositivo en la que debe aplicarse VisualState.

Nota:

El elemento DeviceStateTrigger deriva de la clase StateTriggerBase y, por tanto, puede adjuntar un controlador de eventos al evento IsActiveChanged.

En el siguiente ejemplo de XAML, se muestra una clase Style que incluye objetos DeviceStateTrigger:

<Style x:Key="DeviceStateTriggerPageStyle"
       TargetType="ContentPage">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup>
                <VisualState x:Name="iOS">
                    <VisualState.StateTriggers>
                        <DeviceStateTrigger Device="iOS" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="Silver" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Android">
                    <VisualState.StateTriggers>
                        <DeviceStateTrigger Device="Android" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="#2196F3" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="UWP">
                    <VisualState.StateTriggers>
                        <DeviceStateTrigger Device="UWP" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="Aquamarine" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

En este ejemplo, la clase Style explícita se destina a objetos ContentPage. Los objetos ContentPage que usan el estilo definen su color de fondo en plateado en iOS, en azul pálido en Android y en aguamarina en UWP. En las capturas de pantalla siguientes se muestran las páginas resultantes en iOS y Android:

Captura de pantalla de un cambio de estado visual desencadenado en iOS y Android

Desencadenador de estado de orientación

OrientationStateTrigger desencadena un cambio de VisualState cuando varía la orientación del dispositivo. Este desencadenador tiene una única propiedad enlazable:

Nota:

El elemento OrientationStateTrigger deriva de la clase StateTriggerBase y, por tanto, puede adjuntar un controlador de eventos al evento IsActiveChanged.

En el siguiente ejemplo de XAML, se muestra una clase Style que incluye objetos OrientationStateTrigger:

<Style x:Key="OrientationStateTriggerPageStyle"
       TargetType="ContentPage">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup>
                <VisualState x:Name="Portrait">
                    <VisualState.StateTriggers>
                        <OrientationStateTrigger Orientation="Portrait" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="Silver" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Landscape">
                    <VisualState.StateTriggers>
                        <OrientationStateTrigger Orientation="Landscape" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="White" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

En este ejemplo, la clase Style explícita se destina a objetos ContentPage. Los objetos ContentPage que usan el estilo definen su color de fondo en plateado cuando la orientación es vertical y en blanco cuando la orientación es horizontal.