[Universal App] Uso del Zoom Semantico, creando Jumplists

JumpListIntroducción

Estas envuelto en el desarrollo de una aplicación universal y quieres utilizar un control jumplist, un listado con organizado en grupos que además nos permiten acceder rapidamente a elementos de otros grupos. Si ya has desarrollado previamente para Windows Phone habrás intentando utilizar un contorl LongListSelector pero… ¿que ocurre?, ¿dónde se encuentra?. Las aplicaciones universales tanto Windows como Windows Phone funcionan bajo WinRT donde no tenemos disponible el control. Pero esperad, tranquilos, con la unificación de las plataformas el control LongListSelector de Windows Phone 8 pasa a ser reemplazado por el zoom semántico que ya teníamos en las aplicaciones Windows Store. En este artículo vamos a crear un proyecto universal desde cero y a utilizar en una aplicación el control SemanticZoom para ver como reemplaza al LongListSelecto ren Windows Phone y su uso en Windows Store.

¿Te apuntas?

¿Qué es el Zoom Semántico?

En zoom semántico es la forma que podemos utilizar en nuestras aplicaciones universales a la hora de presentar conjuntos grandes de datos o datos relacionados entre si en una única vista. El control SemanticZoom nos permite mostrar la información al usuario en dos vistas distintas del mismo contenido.

Para hacer zoom semántico, podemos hacerlo mediante un simple gesto (acercando ambos dedos alejamos el zoom, alejando los dedos lo acercamos). Además en Windows podemos pulsar la tecla CTRL del teclado mientras hacemos scroll con el ratón (o pulsamos las teclas + o -).

Veamos un simple ejemplo para dejarlo todo más claro. Imaginaos que estamos desarrollando una aplicación de mensajería. Estamos viendo el historial de mensajes que se ha mantenido con un usuario en concreto. Veremos la vista reducida o alejada:

Vista reducida

Vista reducida

Si queremos tener de un simple vistazo todos los periodos de fechas en los que hemos mantenido mensajes con el usuario bastará con hacer hacer el gesto de acercar los dedos para mostrar la vista ampliada (tras una pequeña animación):

Vista ampliada

Vista ampliada

Como podéis ver en el ejemplo, sin necesidad de hacer scroll podemos mostrar de una simple vistazo la información deseada. Es más, tenemos la posibilidad de añadir información extra. En nuestro ejemplo además de mostrar los distintos periodos en los que se han mantenido conversaciones, se muestra el número de mensajes que se han realizados en cada uno de ellos.

Manos a la obra!

Vamos a crear una aplicación simple que muestre una colección de películas agrupadas por año. El objetivo final será añadir el uso del SemanticZoom para mostrar por defecto la colección de películas y al hacer el zoom out mostrar los distintos años en los que están agrupadas las películas.

Como siempre solemos hacer vamos a realizar un ejemplo lo más simple posible pero que nos sea válida para lograr nuestros objetivos:

Nueva aplicación universal

Nueva aplicación universal

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo. Para probar el control necesitamos una fuente de información. En nuestro ejemplo simplificaremos el proceso al máximo construyendo una colección de elementos en memoria.

Comenzamos creando el modelo de nuestra aplicación:

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

     public string Description { get; set; }

     public string Image { get; set; }

     public int Year { get; set; }
}

Para simplificar el ejemplo, no vamos a conectar nuestra aplicación con internet (rss, servicio web, azure, etc) sino que trabajaremos con datos estáticos. Para ello, en el constructor de nuestra viewmodel creamos un listado de películas:

var movies = new List<Movie>
{    
     new Movie {Title = "John Carter", Year = 2012, Image = "ms-appx:///Assets/Carter.jpg"},
     new Movie {Title = "El caballero oscuro: La leyenda renace", Year = 2012, Image = "ms-appx:///Assets/Batman.jpg"},
     new Movie {Title = "Cisne Negro", Year = 2011, Image = "ms-appx:///Assets/Cisne.jpg"},
     new Movie {Title = "Drive", Year = 2011, Image = "ms-appx:///Assets/Drive.jpg"},
     new Movie {Title = "Toy Story 3", Year = 2010, Image = "ms-appx:///Assets/Toy.jpg"},
     new Movie {Title = "El discurso del rey", Year = 2010, Image = "ms-appx:///Assets/Rey.jpg"},
     new Movie {Title = "Origen", Year = 2010, Image = "ms-appx:///Assets/Origen.jpg"},    
     new Movie {Title = "Avatar", Year = 2009, Image = "ms-appx:///Assets/Avatar.jpg"}
};

NOTA: Las imágenes utilizadas son recursos locales compartidos en una carpeta llamada Assets del proyecto Shared.

Llegamos a este punto podríamos crear una propiedad pública para poder hacer binding (enlazar) desde nuestra interfaz con la colección creada. Sin embargo, dentro de nuestros objetivos está el mostrar la colección de películas agrupadas por año. Además, con el control SemanticZoom queremos mostrar el listado de grupos (años) distintos que contienen películas. Debemos agrupar nuestra colección antes de continuar. Para ello, dentro de nuestra carpeta “Models” anteriormente creada vamos a añadir una nueva clase:

public class MoviesByYear
{
     public int Year { get; set; }
    public List<Movie> Movies { get; set; }
}

Es simple. Almacenará un año junto a la colección de películas de dicho año correspondientes. Tenemos ya donde almacenar la información, vamos a hacerlo.

var moviesByYear = movies.GroupBy(f => f.Year).Select(f => new MoviesByYear { Year = f.Key, Movies = f.ToList() });

Ahora si, creamos una propiedad pública que contendrá la colección de películas agrupadas por año:

public List<MoviesByYear> Movies
{
     get { return _movies; }
     set { _movies = value; }
}

Hemos dejado el view model totalmente preparado. Llega el momento de centrarnos en la vista. Vamos a añadir nuestro control SemanticZoom a nuestra vista MainView:

<SemanticZoom>
     <SemanticZoom.ZoomedInView>

     </SemanticZoom.ZoomedInView>
     <SemanticZoom.ZoomedOutView>

     </SemanticZoom.ZoomedOutView>
</SemanticZoom>

La estructura que puedes ver en la parte superior es la estructura básica a utilizar en un control SemanticZoom. Como puedes observar el control contendrá otros dos controles (normalmente listas). El primero de los controles se añadirá en la vista ZoomedOutview, vista mostrada por defecto. El segundo, se añadirá a la vista ZoomedInView. Vista que mostramos al hacer Zoom Semántico. Ambas vistas están relacionadas semánticamente.

Antes de continuar, veamos algunos miembros importantes del control:

Propiedades

  • ZoomedInView. Vista Zoomed-In del control SemanticZoom.
  • ZoomedOutView. Vista Zoomed-Out del control SemanticZoom.
  • CanChangesView. Determina si el control permite cambiar de vista (Propiedad de sólo lectura).
  • IsZoomedInViewActive. Propiedad de tipo bool nos permite definir con que vista se muestra el control SemanticZoom (ZoomIn o ZoomOut).

Eventos

  • ViewChangeStarted. Se lanza al comenzar el cambio de una vista.
  • ViewChangedCompleted. Se lanza al completarse el cambio de una vista (la nueva vista se muestra).
<SemanticZoom>
     <SemanticZoom.ZoomedInView>
          <ListView>
          </ListView>
     </SemanticZoom.ZoomedInView>
     <SemanticZoom.ZoomedOutView>
          <GridView />
     </SemanticZoom.ZoomedOutView>
</SemanticZoom>

Añadiremos en los recursos de la página un CollectionViewSource que hará binding con nuestra propiedad pública Movies creada en la viewmodel:

<CollectionViewSource
            x:Name="groupedMoviesViewSource"
            Source="{Binding Movies}"
            IsSourceGrouped="true"
            ItemsPath="Movies"/>

Pasamos a definir los DataTemplates:

<DataTemplate x:Key="MovieJumpTemplate">
     <Border Padding="5">
          <Border Background="{Binding Converter={StaticResource BackgroundConverter}}"
                  Width="82" Height="82" HorizontalAlignment="Left">
                <TextBlock Text="{Binding Group.Year}"
                           Foreground="{Binding Converter={StaticResource ForegroundConverter}}"
                           FontSize="24" Padding="6"
                           HorizontalAlignment="Center" VerticalAlignment="Center"/>
          </Border>
     </Border>
</DataTemplate>

<DataTemplate x:Key="MovieItemTemplate">
     <Grid>
          <Grid.ColumnDefinitions>
               <ColumnDefinition Width="Auto"/>
               <ColumnDefinition Width="*"/>
          </Grid.ColumnDefinitions>
          <Image Source="{Binding Image}" Stretch="Uniform" MaxWidth="100"
                 Margin="0, 5"/>
          <TextBlock Grid.Column="1" FontSize="32"
                     Text="{Binding Title}" TextWrapping="WrapWholeWords"
                     Margin="5" />
     </Grid>
</DataTemplate>

<DataTemplate x:Key="MovieGroupHeaderTemplate">
     <Border Background="Transparent" Padding="5">
          <TextBlock Text="{Binding Year}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="32" Padding="6"
                       FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
     </Border>
</DataTemplate>

Necesitamos varias plantillas, en nuestro ejemplo:

  • MovieJumpTemplate: Cada uno de los elementos mostrados en la vista ZoomedOutView del zoom semántico.
  • MovieItemTemplate: Elementos básicos del listado mostrados en la vista por defecto.
  • MovieGroupHeaderTemplate: Cabecera de cada grupo mostrado en la lista por defecto.

NOTA: Creamos una carpeta Themes específica para clada plataforma, en los proyectos Windows y Windows Phone. Aqui añadiremos recursos de diccionarios donde agruparemos los estilos y plantillas específicar a usar en cada plataforma.

Finalmente nuestro zoom semántico quedaría:

<SemanticZoom>
     <SemanticZoom.ZoomedInView>
          <ListView
               IsHoldingEnabled="True"
               ItemsSource="{Binding Source={StaticResource groupedMoviesViewSource}}"
               ItemTemplate="{StaticResource MovieItemTemplate}">
               <ListView.GroupStyle>
                    <GroupStyle HidesIfEmpty="True"
                                HeaderTemplate="{StaticResource MovieGroupHeaderTemplate}">
                    </GroupStyle>
               </ListView.GroupStyle>
          </ListView>
     </SemanticZoom.ZoomedInView>
     <SemanticZoom.ZoomedOutView>
          <GridView ItemsSource="{Binding Source={StaticResource groupedMoviesViewSource}, Path=CollectionGroups}"
                    ItemTemplate="{StaticResource MovieJumpTemplate}"/>
     </SemanticZoom.ZoomedOutView>
</SemanticZoom>

Analicemos el código anterior:

  • Ya hemos visto el funcionamiento del zoom semántico además de conocer sus dos vistas, ZoomedInView y ZoomedOuView.
  • En la vista por defecto mostramos un control ListView. Este control nos permite organizar los elementos en grupos utilizando la propiedad GroupStyle. La apariencia de cada cabecera vendrá definido por la plantilla indicada en la propiedad HeaderTemplate y podemos ocultar cabeceras sin contenido gracias a la propiedad HidesIfEmpty.
  • El JumpList lo conseguimos utilizando el ListView con sus grupos junto al zoom semántico.
  • En la vista ZoomOutView mostramos un GridView bindeado a la misma fuente de información que el ListView, el CollectionViewSource pero accediendo a la propiedad CollectionGroups de la vista que contiene la colección de grupos.

El resultado del ejemplo es el siguiente:

Vista reducida en Windows Phone

Vista reducida en Windows Phone

Podemos ver el listado de películas agrupadas por año (vista ZoomInView), mostrando el año como cabecera. Al pulsar sobre cualquiera de los años:

Vista ampliada en Windows Phone

Vista ampliada en Windows Phone

Pasamos a la vista ZoomOutView viendo todos los años disponibles. La aplicación Windows Store mostrará un resultado similar:

Vista reducida en Windows

Vista reducida en Windows

Mediante un simple gesto (acercando ambos dedos alejamos el zoom, alejando los dedos lo acercamos) o pulsando la tecla CTRL del teclado mientras hacemos scroll con el ratón (o pulsamos las teclas + o -) pasamos a la vista ZoomOutView:

Vista ampliada en Windows

Vista ampliada en Windows

En la vista ZoomOutView vemos el listado de años mostrando además información extra, en este caso el número de películas disponibles en cada año.

Puedes descargar el ejemplo realizado:

Espero que lo visto en la entrada os resulte interesante. Recordar, cualquier duda o sugerencia será bienvenida en los comentarios de la entrada.

Conclusiones

  • El control SemanticZoom forma parte del conjunto de controles WinRT compartido entre Windows y Windows Phone.
  • A pesar de tener el control compartido entre ambas plataformas, en cada una de ellas se adapta para otorgar la experiencia adecuada.
  • El control nos permite trabajar con dos vistas distintas a la hora de mostrar colecciones con un número elevado de elementos. Evita scrolls muy largos, facilita la navegación e interacción entre los elementos.
  • ZoomedInView es la vista por defecto que muestra el Zoom Semántico.
  • ZoomedOutView es la vista mostrada al hacer Zoom Semántico.
  • Para poder utilizar un control en alguna de las vistas del SemanticZoom debe implementar la interfaz ISemanticZoomInfo.
  • Por defecto los controles que heredan de ListViewBase implementan la interfaz ISemanticZoomInfo.
  • CUIDADO: Con el uso del control Listbox que no hereda de ListViewBase y por lo tanto NO implementa ISemanticZoomInfo. No se puede utilizar un control Listbox en una de las vistas del control SemanticZoom.
  • Normalmente se muestra la colección de elementos en la vista ZoomedInView y los grupos de dicha colección en la vista ZoomedOutView. Por ello, también es normal que la fuente de datos del control sea un CollectionViewSource. Aunque no tiene porque ser siempre asi.
  • No tiene una propiedad ItemsSource. No hacemos Binding directamente a una colección de datos. El Binding se realiza desde los controles lista añadidos a cada una de las vistas del SemanticZoom.
  • Por supuesto, el contenido de las distintas vistas pueden hacer scroll.
  • No esta pensado para mostrar una colección y al pulsar sobre un elemento de la misma navegar a otra colección de detalles (Drill-Down). La información mostrada en ambas vistas es la misma.

Artículos relacionados

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