Hoy me gustaría compartir con todos algo que me parece cuanto menos importante e interesante y que hará que nuestras aplicaciones de Windows Store (Metro) tengan un look más atractivo. A lo que me refiero es disponer de elementos de diferentes tamaños en dentro de un GridView sin perder la organización de los mismos tal y como se ve en la aplicación de la Windows Store de Windows 8. Para ilustrar la explicación me referiré a la aplicación de WinPhone metro y explicaré los pasos que seguí para realizar la transformación. Si queréis podéis descargaros el código fuente del ejemplo aquí.

 

  • Vamos a comenzar partiendo de un proyecto basado en la plantilla Aplicación de Cuadrícula o, en inglés, Grid App con lo que ya tendremos datos de ejemplo para poder dar algo de forma a nuestro diseño. Si la ejecutamos directamente tal y como la monta Visual Studio, obtendremos una interfaz muy cuadriculada, sin atractivo y queremos darle algo más de vida a los elementos del GridView.
  • En primer lugar, preparo la estructura en la que organizaré todas las modificaciones. Por ello, he creado una carpeta dentro del proyecto a la que he llamado “Styles” y, a continuación, creo un nuevo diccionario de recursos que llamaré Grid.xaml.
  • El siguiente paso, será agregar la referencia al nuevo diccionario de recursos dentro del fichero App.xaml
           <ResourceDictionary.MergedDictionaries>
 
                <!-- 
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
                <ResourceDictionary Source="Styles/Grid.xaml"/>
            </ResourceDictionary.MergedDictionaries>

 

  • Editamos una copia de la plantilla de elementos (Panel) o en inglés Layout of Items (Panel) del GridView y la ubicaremos en el diccionario de recursos que acabamos de crear, Grid.xaml, donde editaremos el VariableSizedWrapGrid.
  • Al VariableSizedWrapGrid le pondremos un ancho máximo por ejemplo de 750px (MaxWidth=”750”) y vamos a establecer tanto el ancho como el alto de los elementos que va a contener a 250 y 180 píxeles respectivamente (ItemWidth=»250″ ItemHeight=»180″). Con lo que tendríamos algo como esto:

 

<ItemsPanelTemplate x:Key="GroupItemsPanel">
    <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0" MaxWidth="750" ItemWidth="250" ItemHeight="180" />
</ItemsPanelTemplate>

 

  • El siguiente paso que debemos seguir es editar la plantilla de los elementos generados (ItemTemplate) del GridView, y lo haremos editando una copia que ubicaremos también en el diccionario de recursos Grid.xaml.

 

  • De la plantilla que se genera, eliminamos tanto el ancho como el alto del grid, ya que el tamaño de los elementos ya lo estamos controlando desde el VariableSizedWrapGrid. Con lo que la plantilla de los elementos nos quedaría de la siguiente forma:
    <DataTemplate x:Key="GridItemTemplate">
        <Grid HorizontalAlignment="Left" >
            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
                <Image Source="{Binding Image}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
            </Border>
            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
            </StackPanel>
        </Grid>
    </DataTemplate>

 

  • Llegado este punto, toca contar que, para controlar el tamaño de nuestros elementos debemos imaginarnos una matriz o tabla cuyas celdas son del tamaño que especificamos para los elementos del VariableSizedWrapGrid. Entonces, como ejemplo, propongo tener un elemento que ocupe 2 celdas de ancho y 2 celdas de alto, un elemento que ocupe 1 celda de ancho y 2 celdas de alto y, por último, un elemento estándar que ocupe 1 celda tanto de ancho como de alto. ¿Cómo conseguimos esto? Pues bien, nos crearemos tres plantillas de estilo para elementos del tipo GridViewItem y, tal y como  se hace en HTML, vamos a especificar las propiedades ColumnSpan que indica el número de columnas el elemento y RowSpan que indica el número de filas. Por lo tanto, abrimos el fichero Grid.xaml y creamos tres tipos de estilo diferentes. Al primero, GridItemStyleSmall, no es necesario que le establezcamos la propiedad ColumnSpan pero lo he puesto para ilustrar mejor el ejemplo
    <Style x:Key="GridItemStyleSmall" TargetType="GridViewItem">
        <Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="1" />
    </Style>
 
    <Style x:Key="GridItemStyleLarge" TargetType="GridViewItem">
        <Setter Property="VariableSizedWrapGrid.RowSpan" Value="2" />
        <Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="2" />
    </Style>
 
    <Style x:Key="GridItemStyleVertical" TargetType="GridViewItem">
        <Setter Property="VariableSizedWrapGrid.RowSpan" Value="2" />
    </Style>

 

  • El siguiente paso es el más complicado quizás de entender, pero, en definitiva es muy sencillo de implementar. Tenemos que indicar al GridView el estilo de sus elementos pero, como son varios estilos, no podemos hacerlo directamente por lo que tendremos que recurrir a un convertidor de estilos llamado StyleSelector. Creamos una nueva elemento de tipo clase dentro de la carpeta Styles  y le damos el nombre de GroupStyleSelector. El código que debería contener es el siguiente.
using GridTest.Data;
using System;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
 
namespace GridTest.Styles
{
    public class GroupStyleSelector : StyleSelector
    {
        protected override Style SelectStyleCore(object item, DependencyObject container)
        {
            var element = (SampleDataItem)item;
 
            if (element.UniqueId.Contains("Item-1"))
                return Application.Current.Resources["GridItemStyleLarge"] as Style;
            else if (element.UniqueId.Contains("Item-4"))
                return Application.Current.Resources["GridItemStyleVertical"] as Style;
 
            return Application.Current.Resources["GridItemStyleSmall"] as Style;
        }
    }
}

Las condiciones de los “bloques if” dependerán de a qué elementos queremos aplicar qué plantillas. En este caso que estamos haciendo uso de la plantilla de aplicación de tipo Grid que viene por defecto en Visual Studio, los elementos que se usan para mostrar datos en el GridView tienen un campo UniqueId que está compuesto por Grupo-X-Item-Y, con lo que yo estoy condicionando por el primero y por el cuarto elemento de cada grupo a los que aplicaré los estilos GridItemStyleLarge y GridItemStyleVertical respectivamente.

  • Nos falta un último paso para que todo esté listo, y no es más que hacer que el GridView use nuestro GroupStyleSelector para seleccionar la plantilla que tendrá cada elemento en cuestión. Para hacerlo, abriremos el fichero App.xaml y añadiremos una referencia a este archivo como contenido de un ResourceDictionary.
           <ResourceDictionary.MergedDictionaries>
                <!-- 
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
                <ResourceDictionary Source="Styles/Grid.xaml"/>
                <ResourceDictionary>
                    <styles:GroupStyleSelector x:Key="GroupStyleSelector" />
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>

  • Por último, ahora que ya el código XAML es capaz de acceder al selector de estilos que hemos creado, se lo asignamos al GridView añadiéndole la propiedad ItemContainerStyleSelector y le indicamos que debe acceder al recurso GroupStyleSelector.
        <GridView
            x:Name="itemGridView"
            AutomationProperties.AutomationId="ItemGridView"
            AutomationProperties.Name="Grouped Items"
            Grid.RowSpan="2"
            Padding="116,137,40,46"
            ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
            ItemTemplate="{StaticResource GridItemTemplate}"
            ItemContainerStyleSelector="{StaticResource GroupStyleSelector}" 
            SelectionMode="None"
            IsSwipeEnabled="false"
            IsItemClickEnabled="True"
            ItemClick="ItemView_ItemClick">

  • Y… todo listo para mostrar elementos de tamaño variable dentro de nuestro GridView. El resultado lo dice todo.

Código Fuente | GridTest

download