[Windows Phone 8.1] Trio de ases: Behaviors, Animations y VisualStates (3)

Y llegamos al último artículo de esta serie, tras ver como trabajar con behaviors y animaciones, en esta ocasión vamos a hablar de los estados visuales.

Los VisualStates nos permiten definir diferentes estados para nuestra página o control para cambiar su apariencia e incluso su funcionalidad. Por ejemplo en una página donde el usuario puede ver un resumen de datos y tiene la opción de editarlos, podemos crear un VisualState de solo lectura y otro de edición, cambiando propiedades de los controles en cada uno de ellos para modificar la forma en la que el usuario interactúa con la página.

También, como vamos a ver en el ejemplo de este artículo, podemos crear estados visuales que modifiquen la interface de usuario dependiendo de la orientación del dispositivo. De esta forma podemos ofrecer al usuario la mejor experiencia en cada momento.

Los VisualStates usan animaciones para modificar los controles a los que quieren afectar y podemos usar behaviors para iniciar el proceso de cambio de uno a otro.

Por regla general, los VisualStates de una página se definirán en la primera Grid que contenga la página. Para ello incorporaremos el nodo, que debe ser el primero dentro de la Grid, VisualStateManager para definir los grupos y los estados de cada grupo:

VisualStateManager
<Grid>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="LayoutStates">
            <VisualState x:Name="Landscape">

            </VisualState>
            <VisualState x:Name="Portrait">

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

</Grid>

Tras definir el nodo VisualStateManager.VisualStateGroups, definimos los grupos que necesitemos, en este caso uno. El nombre, LayoutStates, es libre y a nuestra elección. Por claridad deberíamos indicar un nombre descriptivo de la finalidad del grupo. En este caso este grupo contiene dos estados, Landscape y Portrait, nombres también a nuestra elección. Si quisiésemos tener otro grupo, podríamos ponerlo a continuación de este con sus correspondientes estados.

En este ejemplo, tenemos una lista de imágenes y queremos variar la forma de mostrarlas al usuario dependiendo de la orientación del dispositivo. Cuando estemos en horizontal, queremos usar un FlipView que haga uso de toda la pantalla. Por el contrario cuando la orientación sea vertical, queremos usar una GridView que muestre las imágenes en una cuadrícula. Así mismo, cuando estemos en vertical queremos que al hacer click en las imágenes aparezca un popup con la imagen seleccionada. Nuestro XAML quedaría más o menos así:

 

XAML
<GridView x:Name="ListImages" ItemsSource="{Binding Images}">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Width="150" Height="150" Margin="0">
                <Image Stretch="UniformToFill" Source="{Binding }"/>
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="SelectionChanged">
            <core:GoToStateAction StateName="BigImage"/>
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</GridView>

<Grid x:Name="Popup" Visibility="Collapsed" Background="#AA555555" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
    <Image Margin="40" Stretch="Uniform" Source="{Binding SelectedItem, ElementName=ListImages}">
    </Image>
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Tapped">
            <core:GoToStateAction StateName="NormalState"/>
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Grid>

<FlipView x:Name="flipImages" Visibility="Collapsed" ItemsSource="{Binding Images}" SelectedItem="{Binding SelectedItem, ElementName=ListImages}">
    <FlipView.ItemTemplate>
        <DataTemplate>
            <Image Stretch="Uniform" Source="{Binding }"></Image>
        </DataTemplate>
    </FlipView.ItemTemplate>
</FlipView>

 

Como vemos, simplemente tenemos todos los controles necesarios: un GridView, una Grid llamada Popup y por último un FlipView. Por defecto tanto el popup como el FlipView tienen su visibilidad establecida a Collapsed. Lo que vamos a hacer es, en cada VisualState, mostrar u ocultar los controles que nos interesen en cada momento:

VisualState Landscape
<VisualState x:Name="Landscape">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ListImages" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="flipImages" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>

 

VisualState Portrait
<VisualState x:Name="Portrait">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ListImages" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="flipImages" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>

 

Así, en el caso del VisualState Landscape, cambiamos la visibilidad del GridView a Collapsed y la del FlipView a Visibile. En el Portrait lo contrario, mostramos el GridView y ocultamos el FlipView.

¿Y como lanzamos cada VisualState? Tenemos que hacerlo manualmente. Detectar el cambio de orientación y cambiar al VisualState que corresponda. Aquí es donde los behaviors vienen en nuestra ayuda. En concreto vamos a servirnos del Behavior DataTriggerBehavior. En nuestra ViewModel vamos a acceder a la clase DisplayInformation, que nos ofrece la propiedad CurrentOrientation y el evento OrientationChanged. Analizando los datos que nos ofrecen vamos a exponer una propiedad llamada IsLandscape:

DisplayInformation
public VMMain()
{
    var dInfo = DisplayInformation.GetForCurrentView();
    if (dInfo.CurrentOrientation == DisplayOrientations.Landscape || dInfo.CurrentOrientation == DisplayOrientations.LandscapeFlipped)
        IsLandscape = true;
    else
        IsLandscape = false;

    dInfo.OrientationChanged += dInfo_OrientationChanged;
}

Luego en XAML, usaremos el DataTriggerBehavior para ver si esta propiedad es true, momento en el que usaremos el GoToStateAction para cambiar al estado Landscape, o false, para cambiar al estado Portrait:

DataTriggerBehavior
<i:Interaction.Behaviors>
    <core:DataTriggerBehavior Binding="{Binding IsLandscape}" ComparisonCondition="Equal" Value="true">
        <core:GoToStateAction StateName="Landscape"/>
    </core:DataTriggerBehavior>
    <core:DataTriggerBehavior Binding="{Binding IsLandscape}" ComparisonCondition="Equal" Value="false">
        <core:GoToStateAction StateName="Portrait"/>
    </core:DataTriggerBehavior>
</i:Interaction.Behaviors>

Y Voila! Si ejecutamos nuestra aplicación en el simulador de Windows 8.1 o el emulador de Windows Phone 8.1, veremos que al cambiar la orientación, automáticamente cambia la apariencia de nuestra aplicación. Además, como todo lo visto en los artículos anteriores, los VisualStates son también totalmente compatibles entre Windows Phone y Windows Store, por lo que el ejemplo incluido en este artículo es una aplicación universal que comparte el 100% del código C# y XAML usado:

image

Las aplicaciones de los VisualStates son muchas. Pero lo más importante es que nos ofrecen una forma sencilla de cambiar el aspecto de nuestra página. De echo, si editas la plantilla por defecto de cualquier control de XAML, verás que usa VisualStates para definir estados como Focus, Tapped, o Selected.

Y con esto llegamos al final de este artículo y de la serie. Espero que haya sido útil para ver una forma nueva de trabajar con el aspecto visual de nuestra página, lanzar acciones en respuesta a las del usuario y crear bonitas animaciones. Como siempre, aquí puedes descargar este último ejemplo para usarlo como referencia.

Un saludo y Happy Coding!!

 

Published 17/6/2014 5:56 por Josué Yeray Julián Ferreiro
Comparte este post:

Comentarios

# [Windows Phone 8.1] Nokia SensorCore SDK

Thursday, July 10, 2014 9:11 AM por Josue Yeray

El martes 24 de junio pasado Nokia presentó y puso a nuestra disposición su último