WinRT. Cómo implementar Snap View.

Introducción

Una de las características sin duda más interesantes planteadas por Windows 8 es la posibilidad de compartir pantalla por dos aplicaciones al mismo tiempo.

Para conseguir dividir la pantalla y compartir dos aplicaciones de manera simultánea hay varias opciones diferentes. Sin duda, la más fácil de todas es colocar el cursor del ratón en la parte superior de una aplicación hasta que el icono del cursor cambia al icono de la mano. En ese momento, seleccionar la aplicación haciendo Clic y manteniendolo arrastrarla a izquierda o derecha. De esta forma la aplicación se quedará en una vista reducida dejando el hueco restante disponible. Si pulsamos la tecla Windows y abrimos otra aplicación, pasará a ocupar el espacio restante. De esta forma logramos tener dos aplicaciones en pantalla de manera simultánea.

Otra forma de dividir la pantalla entre dos aplicaciones es llevar el ratón a la parte superior derecha de la pantalla. Aparecerá una miniatura de la aplicación más reciente que hayamos utilizado. Si a continuación movemos el ratón hacia abajo tendremos el listado de las aplicaciones utilizadas. Elegimos una aplicación, la seleccionamos y manteniendo pulsado el botón izquierdo la arrastramos a izquierda o derecha.

Por último, también podemos colocar una aplicación a pantalla dividida utilizando el teclado. Para ello se utilizará la combinación de teclado Windows + . (punto). Pulsando dicha combinación utilizando una aplicación Windows Store a pantalla completa colocaremos la aplicación a la derecha, posteriormente y tras volver a utilizar la combinación a la izquierda y al pulsar de nuevo pasaría a volver a pantalla completa.

NOTA: Para poder realizar esta operación la resolución del sistema debe ser de 1366×768 px o superior.

Por nuestra parte, los desarrolladores, debemos tener en cuenta que la implementación de la vista Snapped es obligatoria. Una buena impementación de la vista Snapped suele proporcionar bastantes puntos a la aplicación. Dependiendo del tipo de aplicación tendrá mucha importancia (cliente Twitter por ejemplo) o carecerá de sentido (un ejemplo que podéis probar es la propia Tienda).

En la entrada actual vamos a realizar paso a paso la implementación de la vista Snapped en una aplicación Windows Store.

Implementar Snap View

Como siempre solemos hacer vamos a realizar un ejemplo lo más simple posible pero que nos sea válida para lograr nuestros objetivos. La plantilla selecciona para realizar el ejemplo lo más simple posible será “Blank Application”:

Vamos a crear una aplicación simple que muestre una colección de películas donde trabajaremos en los distintos layouts para las distintas vistas.

Comenzamos. Nos centramos primero en el modelo de la aplicación. Vamos a añadir a nuestro proyecto una nueva clase donde definiremos el objeto Film:

public class Film
{
     public string Title { get; set; }

     public string Image { get; set; }
}

Tras crear el modelo, vamos a crear el view model que proporcionará la información requerida en nuestra vista. En el ejemplo que puedes encontrar al final de esta entrada se le ha llamado SnapViewModel. Para simplificar el ejemplo nuestra aplicación utilizará datos estáticos (no conectaremos con ningún servicio web externo):

class SnapViewModel
{
     public List Items { get; set; }

     public SnapViewModel()
     {
          var films = new List
          {
               new Film {Title = "Cadena perpetua", Image = "http://pics.filmaffinity.com/Cadena_perpetua-576140557-large.jpg"},
               new Film {Title = "El padrino", Image = "http://pics.filmaffinity.com/El_Padrino-485345341-large.jpg"},
               new Film {Title = "El padrino. Parte II", Image = "http://pics.filmaffinity.com/El_Padrino_Parte_II-807355469-large.jpg"},
               new Film {Title = "Pulp Fiction", Image = "http://pics.filmaffinity.com/Pulp_Fiction-586496431-large.jpg"},
               new Film {Title = "El bueno, el feo y el malo",Image = "http://pics.filmaffinity.com/El_bueno_el_feo_y_el_malo-589330734-large.jpg"},
               new Film {Title = "12 hombres sin piedad", Image = "http://pics.filmaffinity.com/12_hombres_sin_piedad_Doce_hombres_sin_piedad-290572645-large.jpg"},
               new Film {Title = "La lista de Schindler", Image = "http://pics.filmaffinity.com/La_lista_de_Schindler-803188900-large.jpg"},
               new Film {Title = "El caballero oscuro", Image = "http://pics.filmaffinity.com/El_caballero_oscuro-102763119-large.jpg"},
               new Film {Title = "El señor de los anillos: El retorno del rey", Image = "http://pics.filmaffinity.com/National_Geographic_Beyond_the_Movie_El_Senor_de_los_Anillos_El_Retorno_del_Rey_TV-916880961-large.jpg"},
          };

          Items = films;
     }
}

Fácil, ¿verdad?

Hemos creado una colección de objetos Film y tenemos preparada una propiedad pública Items que utilizaremos en nuestra interfaz. Debemos vincular la vista con su correspondiente view model, para ello:

public SnapView()
{
     this.InitializeComponent();

     DataContext = new SnapViewModel();
}

Ok. Pasamos a definir la vista. Lo primero será preparar el acceso a la colección de datos. Para ello vamos a añadir un CollectionViewSource en los recursos de la página:

<Page.Resources>
</Page.Resources>

El CollectionViewSource tendra como fuente la propiedad pública Items creada en el view model:

<CollectionViewSource
     x:Name="itemsViewSource"
     Source="{Binding Items}"/>

Lo tenemos casi todo listo. Por último, añadiremos un control GridView que utilizará el CollectionViewSource añadido en los recursos:

<GridView
     x:Name="itemGridView"
     AutomationProperties.AutomationId="ItemsGridView"
     AutomationProperties.Name="Items"
     TabIndex="1"
     Padding="116,136,116,46"
     ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
     ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
     SelectionMode="None"
     IsSwipeEnabled="false"/>

GridView

Tras ejecutar la aplicación visualizaremos algo similar a lo que puedes observar en la captura superior. Si intentamos pasar a la vista Snapped (utilizando alguna de las formas descritas en la introducción de la entrada) veremos que es imposible.

¿Por que?

Sencillo. Debemos implementar la vista y es imposible en una página. Hemos elegido la plantilla “Aplicación vacía” que nos añade una MainPage (Página en blanco). Una página en blanco contiene la mínima cantidad de XAML y código para crear una instancia de una Page. El resto de plantillas de Page incluyen código adicional y clases auxiliares que nos ayudarán tanto en el uso de estilos como con el tratamiento del layout.

Por lo tanto, lo más fácil para implementar la vista Snapped es sin duda añadir una nueva página en blanco al proyecto:

Página básica

Tras añadir la página en blanco (eliminamos la página que ya teníamos creada, recuerda modificar en el App.cs hacia que página se navega de inicio en el método OnLaunched), se nos preguntará lo siguiente:

Tras añadir la página en blanco

Contestamos Sí a la pregunta. Sencillamente nos va a añadir de manera automática código adicional repartido en un conjunto de clases auxiliares. La estructura del proyecto añadida (carpeta Common) quedaría:

Estructura añadida al proyecto

La nueva página será LayoutAwarePage. Entre otros detalles, nos añade el botón Atrás de navegación, el título y todo lo necesario para controlar las distintas vistas posibles:

<common:LayoutAwarePage>
</common:LayoutAwarePage>

Añadimos el mismo GridView que utilizamos anteriormente:

<GridView
     x:Name="itemGridView"
     AutomationProperties.AutomationId="ItemsGridView"
     AutomationProperties.Name="Items"
     TabIndex="1"
     Grid.RowSpan="2"
     Padding="116,136,116,46"
     ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
     ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
     SelectionMode="None"
     IsSwipeEnabled="false"/>

Añadiremos también un ListView (fijáos que la visibilidad por defecto es oculto, en caso contrario nos aparecería tanto el GridView como el ListView superpuesto):

<ListView
     x:Name="itemListView"
     AutomationProperties.AutomationId="ItemsListView"
     AutomationProperties.Name="Items"
     TabIndex="1"
     Grid.Row="1"
     Visibility="Collapsed"
     Margin="0,-10,0,0"
     Padding="10,0,0,60"
     ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
     ItemTemplate="{StaticResource Standard80ItemTemplate}"
     SelectionMode="None"
     IsSwipeEnabled="false"/>

¿Para que hemos añadido un ListView?

Para aquellos que ya hayan trabajado a fondo en interfaces de usuario realizadas con XAML en WPF o Silverlight posiblemente conozcan los estados visuales (Visual States). Básicamente, los estados visuales nos permiten trabajar con la interactividad de nuestra aplicación. Nos permite definir una apariencia visual diferente para cada estado. En WinRT las distintas vistas se controlan con estados visuales. De modo que cuando se detecta que pasamos a la vista Snapped ocultamos el GridView y mostramos el ListView (por eso lo añadimos). Al pasar de la vista Snapped a pantalla completa, se realiza el proceso inverso.

<VisualStateManager.VisualStateGroups>
</VisualStateManager.VisualStateGroups>

Tenemos disponibles los siguientes estados:

  • FullScreenLandscape
  • FullScreenPortrait
  • Filled
  • Snapped

En nuestro ejemplo distinguiremos basicamente dos estados:

  • Snapped
  • El resto

Cuando no estamos en la vista Snapped:

<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<VisualState x:Name="FullScreenPortrait">
     <Storyboard>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
               <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
          </ObjectAnimationUsingKeyFrames>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView" Storyboard.TargetProperty="Padding">
               <DiscreteObjectKeyFrame KeyTime="0" Value="96,136,86,56"/>
          </ObjectAnimationUsingKeyFrames>
     </Storyboard>
</VisualState>

Basicamente mostramos el GridView y mostramos el botón volver (en caso necesario) estándar.

Vamos a definir que acciones realizamos al pasar a la vista Snapped:

<VisualState x:Name="Snapped">
     <Storyboard>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
               <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
          </ObjectAnimationUsingKeyFrames>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
               <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
          </ObjectAnimationUsingKeyFrames>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Visibility">
               <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
          </ObjectAnimationUsingKeyFrames>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView" Storyboard.TargetProperty="Visibility">
               <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
          </ObjectAnimationUsingKeyFrames>
     </Storyboard>
</VisualState>

Realizamos las siguientes acciones:

  • Cambiamos el estilo del botón back por el estilo Snapped definido en los recursos de estilos que tenemos en la carpeta Common.
  • Cambiamos el estilo del título.
  • Mostramos el ListView.
  • Ocultamos el GridView.

El resultado conseguido lo podéis ver a continuación:

FullScreen LandScape

Snapped

Puedes ver el resultado conseguido en este ejemplo en movimiento a continuación:

También puedes descargar el ejemplo realizado:

Nada más por hoy. Recordar que cualquier duda o sugerencia podéis plantearlas en los comentarios.

Más información

Extra

No podemos forzar programáticamente a la vista Snapped desde código. Debe ser siempre una acción realizada por el usuario. Sin embargo, si contamos con la posibilidad de forzar la recuperación de la vista a pantalla completa desde el modo Snapped:

Windows.UI.ViewManagement.ApplicationView.TryUnsnap();

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