[Windows 10] Adaptive Triggers

Code - 02Introducción

En la plataforma Windows contámos con una gran variedad de dispositivos, PCs, tabletas, Phablets, teléfonos. Contamos con una gran cantidad diversa de tamaños de pantalla, resoluciones y otros factores que como desarrolladores debemos tener en cuenta. El usuario es exigente, debe serlo. Es vital poder disfrutar de la mejor experiencia posible en cada uno de los dispositivos, y ese, es uno de nuestros retos.

Adaptando la interfaz

Adaptar la interfaz de usuario a distintos tamaños, orientaciones y otros cambios no es algo nuevo. Con la llegada de las Apps Modern UI en Windows 8.0 debíamos gestionar la vista Narrow. Con las Apps Universales en Windows 8.1 desarrollábamos Apps con Windows Phone y Windows en mente. Ante este tipo de situaciones, hasta ahora hemos utilizado diferentes técnicas. Las más habituales son:

  • Vistas XAML separadas por plataforma.
  • Diseño flexible. Utilizar posiciones y tamaños relativos para permitir escalar facilmente.
  • Utilizar diferentes estados visuales (Visual States) para gestionar vistas en diferentes dispositivos, pantallas e incluso orientaciones.

Adaptive Triggers

Con la llegada del SDK de Windows 10 Preview tenemos la posibilidad de crear Apps Universales con un único binario que funcione en múltiples plataformas. Es un paso importante pero que conlleva realizar una acción que sera comun, diferenciar entre las diferentes plataformas donde correrá dicho binario para poder adaptar la interfaz de usuario. Con ese objetivo llegan los Adaptive Triggers. Recibimos potentes novedades en los Visual States:

  • Podemos modificar propiedades sin necesidad de animaciones. Anteriormente, la síntaxis era mucho más verbosa y necesitábamos utilizar StoryBoards para cambiar cualquier propiedad por simple que fuese.
  • Contamos con los StateTrigger. Podemos lanzar Triggers cuando se aplica un estado visual sin necesidad de código adyacente en el code-behind. El concepto es muy similar a los Triggers que tenemos disponible en WPF y Silverlight y el objetivo es el mismo, realizar un cambio cuando una condición cambia.

Actualmente contamos con un tipo de StateTrigger por defecto, el Adaptive Trigger que cuenta con los siguientes tipos:

  • MinWindowWidth
  • MinWindowHeight

Ambos nos permiten detectar cambios dinámicos en el tamaño de la pantalla de modo que se adapte el tamaño de la pantalla entre pantallas pequeñas y grandes. El funcionamiento es similar a las media queries en CSS por ejemplo. Se crearán diferentes estados estableciendo unos altos y anchos mínimos, de modo que, cuando la pantalla es más grande que el tamaño asignado se activará el estado visual.

NOTA: Los valores se especifican en pixeles.

Para probar las posibilidades de los Adaptive Triggers crearemos un nuevo proyecto UAP:

Nueva App UAP

Nueva App UAP

Nuestro objetivo en el ejemplo serán:

  • Aprender a utilizar AdaptiveTrigger de la forma más sencilla posible. Utilizaremos un texto que modificaremos segun ciertas condiciones del ancho de la ventana.
  • Crearemos un StateTrigger personalizado.

Añadimos en nuestra infertaz un control para mostrar texto:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
     <TextBlock x:Name="Info"
                HorizontalAlignment="Center"
        VerticalAlignment="Center" />
</Grid>

Sera el control que modificaremos mediante el VisualState:

<!-- Using AdaptiveTrigger -->
<VisualState x:Name="Small">
     <VisualState.StateTriggers>
          <AdaptiveTrigger MinWindowWidth="0" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Pantalla pequeña" />
          <Setter Target="Info.FontSize" Value="32" />
          <Setter Target="Info.Foreground" Value="Red" />
     </VisualState.Setters>
</VisualState>
<VisualState x:Name="Big">
     <VisualState.StateTriggers>
          <AdaptiveTrigger MinWindowWidth="600" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Pantalla grande" />
          <Setter Target="Info.FontSize" Value="48" />
          <Setter Target="Info.Foreground" Value="Blue" />
     </VisualState.Setters>
</VisualState>

Analicemos el trozo de XAML de la parte superior. Hemos definido dos estados visuales donde utilizamos la propiedad MinWindowWidth del StateTrigger AdaptiveTrigger para aplicar una serie de cambios en las propiedades del control TextBlock Info. Podemos encadenar tantos Setters como sean necesarios y no es necesario la gestión por medio de StoryBoards lo que nos permite definirlo todo de una manera menos verbosa.

Si lanzamos nuestra App y el tamaño de la ventana es superior a 600 px veremos algo como lo siguiente:

Estado Visual "Big"

Estado Visual “Big”

Mientras que si reducimos el tamaño:

Estado Visual "Small"

Estado Visual “Small”

Muy potente, ¿verdad?. De esta forma podemos hacer distintos cambios en la organización de elementos, estilos y otros detalles para ajustar la interfaz de usuario segun el tamaño de la ventana.

Creando Adaptive Triggers personalizados

Hasta ahora hemos aprendido que:

  • Tenemos potentes novedades en los VisualStates.
  • Como gestionar los elementos de la interfaz de usuario para adaptar la interfaz de usuario a cada dispotivo.
  • El uso de Adaptative Triggers para controlar el ancho y/o alto de la pantalla y adaptar la UI.

Los Triggers disponibles son lo suficientemente potentes como para permitirnos un trabajo correcto adaptando la interfaz de usuario pero… ¿podemos llegar más lejos?, ¿y si no es suficiente?

Si analizamos la clase AdaptiveTrigger utilizada hasta ahora:

AdaptiveTrigger

AdaptiveTrigger

Vemos que hereda de una clase base llamada StateTriggerBase disponible dentro del namespace Windows.UI.Xaml.

Podemos crear nuestros propios StateTriggers.

Crearíamos nuevas clases heredando de la clase mencionada. Vamos a crear un nuevo StateTrigger que nos indique en que plataforma estamos (Windows o Windows Phone).

Creamos una clase que herede de StateTriggerBase:

public class PlatformStateTrigger : StateTriggerBase
{

}

Al igual que en AdaptiveTrigger contamos con dos propiedades, MinWindowWidth y MinWindowHeight que nos permiten lanzar el Trigger dependiendo de condiciones utilizando las mismas.

En nuestro StateTrigger crearemos una propiedad de dependencia llamada Platform que nos permitirá pasar el nombre de la plataforma, de modo que si estamos en dicha plataforma lanzar la condición:

public string Platform
{
     get { return (string)GetValue(PlatformProperty); }
     set { SetValue(PlatformProperty, value); }
}

public static readonly DependencyProperty PlatformProperty =
     DependencyProperty.Register("Platform", typeof(string), typeof(PlatformStateTrigger),
     new PropertyMetadata(true, OnPlatformPropertyChanged));

private static void OnPlatformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{

}

La parte más importante del Trigger es determinar con exactitud la plataforma en la que se ejecuta la App. Entre las opciones disponibles, la más sencilla es utilizar ResourceContext:

var qualifiers = ResourceContext.GetForCurrentView().QualifierValues;
_deviceFamily = qualifiers.First(q => q.Key.Equals("DeviceFamily")).Value;

Accedemos al listado de QualifierValues, un IObservableMap con claves y valor que contiene información como el idioma de la App, el contraste, el tema utilizado o la familia del dispositivo. Utilizaremos esta última para determinar si estamos en Desktop o Phone.

De modo que ante el cambio de nuestra propiedad:

private static void OnPlatformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
     var obj = (PlatformStateTrigger)d;
     var platform = (string)e.NewValue;

     if(_deviceFamily.Equals("Desktop"))
    obj.SetTriggerValue(platform == "Windows");
     else
    obj.SetTriggerValue(platform == "WindowsPhone");
}

Nuestro objetivo final es llamar al método SetTriggerValue(bool) que es el encargado de determinar si la condicion del estado se cumple o no. Dependiendo del dispositivo lanzamos la condición correcta.

Por último, queda utilizar nuestro StateTrigger personalizado. En nuestra interfaz declaramos antes que nada el namespace donde lo hemos definido:

xmlns:customStateTriggers="using:AdaptiveTriggers.CustomStateTriggers"

Podemos crear VisualStates utilizando PlatformStateTrigger:

<VisualState x:Name="WindowsPhone">
     <VisualState.StateTriggers>
          <customStateTriggers:PlatformStateTrigger Platform="WindowsPhone" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Windows Phone" />
          <Setter Target="Info.FontSize" Value="32" />
          <Setter Target="Info.Foreground" Value="Red" />
     </VisualState.Setters>
</VisualState>
<VisualState x:Name="Windows">
     <VisualState.StateTriggers>
          <customStateTriggers:PlatformStateTrigger Platform="Windows" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Windows" />
          <Setter Target="Info.FontSize" Value="48" />
          <Setter Target="Info.Foreground" Value="Blue" />
     </VisualState.Setters>
</VisualState>

En el caso de estar en un dispositivo Windows Phone se lanzaría el segundo estado visual, en el de Windows el primero, adaptando la interfaz de usuario (en nuestro ejemplo un simple TextBlock).

De igual forma podemos crear Adaptive Triggers personalizados para gestionar aspectos como el tipo de dispositivo, el tipo de orientación, haciendo verificaciones de condiciones como datos, internet, etc.

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s