Microsoft MVP

Email y Rss

email rss

Klout

Seguidores en facebook

Timeline de mi Twitter

Tienes preguntas?

Ideas de un Conejo
Más allá de los sistemas de información: (C#)=> videojuegos, soluciones a problemas interesantes y Sistemas Operativos
XNA
C#
Sistemas Operativos
Varios
Windows Phone
WinRT
XAML
Azure
HTML 5
Acerca de

Cómo crear agrupamiento utilizando listas en WinRT? XAML- C#

August 19th, 2012 by JuanK

Follow @JuanKRuiz

Es un tema en esencia sencillo, tengo un conjunto de datos y quiero mostrarlos de manera agrupada por un criterio. Sin embargo para implementarlo hay que tener en cuenta varios tips adicionales, más allá de la documentación de msdn.

Los pasos a seguir son:

  • Crear el origen de datos agrupados
  • Instanciar el origen de datos desde el XAML
  • Modificar el template del control para que soporte grouping
  • Modificar el template para optimizar la visualización de los grupos

Crear el origen de datos agrupados

Los primero es que debes tener ya establecido un conjunto de datos, ¿necesitas datos de prueba? “C# – Cómo generar datos aleatorios para realizar pruebas”.

Trabajaremos con una lista de personas como estas, tienen implementado de una vez INotifyPropertyChanged (BindableBase) para utilizarla al hacer Binding:

public class Persona : BindableBase
{
    private int _cedula;
    public int Cedula
    {
        get { return _cedula; }
        set
        {
            _cedula = value;
            SetProperty(ref _cedula, value);
        }
    }

    private string _nombre;
    public string Nombre
    {
        get { return _nombre; }
        set
        {
            _nombre = value;
            SetProperty(ref _nombre, value);
        }
    }

    private string _apellido;
    public string Apellido
    {
        get { return _apellido; }
        set { SetProperty(ref _apellido, value); }
    }

    private string _profesion;
    public string Profesion
    {
        get { return _profesion; }
        set { SetProperty(ref _profesion, value); }
    }
}

Ahora tenemos que pensar en el agrupamiento, agruparemos a las personas por profesión así que creamos un objeto capaz de guardar grupos de personas, utilizo ObservableCollectionsolo para tener la funcionalidad completa en el binding.

public class GrupoPersonas
{
    public string Profesion { get; set; }
    public List< Persona> ListaPersonas { get; set; }
}

Esta lista la podemos llenar tomando como base nuestra fuente de datos, extraeremos los datos utilizando linq y para todo ello crearemos una clase encargada de administrar dichos datos.

public class DataSourcePersonas
{
    public ObservableCollection<Persona> ListaPersonas { get; set; }

    public DataSourcePersonas()
    {
        var listaFull = Traer‌InfoDesdeOrigenDeDatos();
        ListaPersonas = new ObservableCollection(listaFull);
    }

    public ObservableCollection ListaPersonasAgrupada { get; set; }
    public void Initialize()
    {
        var lista = from persona in ListaPersonas
                                    group persona by persona.Profesion into grupo
                                    select new GrupoPersonas()
                                    {
                                        Profesion = grupo.Key,
                                        ListaPersonas = grupo.ToList()
                                    };
        ListaPersonasAgrupada = new ObservableCollection<Persona>(lista);
    }
}

Analicemos el código anterior, esta clase nos permite acceder a nuestro origen de datos normal, llamado ListaPersonas, y también nos permite acceder a nuestro origen de datos agrupado llamado ListaPersonasAgrupado, los datos de este último los obtenemos consultando ListaPersonas y creando los grupos a traves de linq. Ambas listas las hemos creado como ObservableCollectionpara así sacar máximo provecho durante el binding.

Instanciar el origen de datos desde el XAML

Ahora desde XAML debemos instanciar nuestro origen de datos, pero no tan rápido vaquero!
Para que podamos utilizar el origen de datos para presentarlos de manera agrupada debemos hacerlo a través de un objeto CollectionViewSource, así que debemos instanciar realmente dos objetos desde XAML, primero DataSourcePersonas y luego un CollectionViewSource al cual debemos asignarle como fuente de datos agrupados a DataSourcePersonas:

<Page
    xmlns:data="using:DemoGrouping.Data">
    <Page.Resources>
        <data:DataSourcePersonas x:Key="DataSourcePersonas"/>
        <CollectionViewSource x:Key="CvsGruposPersonas" IsSourceGrouped="True"
                              Source="{Binding Source={StaticResource DataSourcePersonas},
                                               Path=ListaPersonasAgrupada}"
                              ItemsPath="ListaPersonas" />
    </Page.Resources>
</Page>

CollectionViewSource aparenta ser complejo, pero es muy sencillo de entender, básicamente permite generar una vista sobre un conjunto de datos para aplicarles ordenamientos, agrupamientos y filtros, el atributo IsSourceGrouped es utilizado para indicar que el CollectionViewSource pose un conjunto de datos agrupados, bien podría no serlo pero para este ejemplo como adivinaras esto es requerido.

Source es la propiedad que indica de donde se deben tomar los datos, en este caso los tomamos de un recurso creado dentro del XAML que no es más que DataSourcePersonas al cual por conveniencia le hemos puesto como key el mismo nombre del objeto; es de notar que DataSourcePersonas no es el directo origen de datos, los datos están en una de sus propiedades públicas llamada ListaPersonasAgrupada, así lo definimos cuando lo creamos (más arriba),por eso en el binding del Source del CollectionViewSource hacemos Binding con DataSourcePersonas pero le indicamos por la Propiedad Path cual es el objeto que debemos considerar como origen de datos, es decir la lista de grupos de personas.

Finalmente en el CollectionViewSource asignamos el atributo ItemsPath que? luego ya no habíamos asignado los ítems?
Si, pero los items que asignamos son los grupos de Personas y cada grupo dentro de si tiene otros atributos, si revisamos nuestro GrupoPersonas podemos darnos cuenta que hay dos atributos:

public class GrupoPersonas
{
    public string Profesion { get; set; }
    public List< Persona> ListaPersonas { get; set; }
}

El CollectionViewSource necesita saber cual de esos atributos contiene las personas de cada grupo de personas, eso es ItemsPath.

Modificar el template del control para que soporte grouping

Estamos listos para ahora solo preocuparnos por la UI.

Para ello debemos hacer Binding de la colección de datos sobre el contenedor de lista, si no conoces del tema o no lo has hecho antes talvez te convenga leer primero este artículo:

Cómo utilizar controles de lista para mostrar colecciones de datos en WinRT? – C# – XAML

Por facilidad les dejo acá todos los estilos que se utilizaran, son solo para ponerle algo de color y de orden, no afectan en nada más nuestra tarea.

<Style x:Key="apptile" TargetType="StackPanel">
    <Setter Property="Margin" Value="5"/>
    <Setter Property="Background">
        <Setter.Value>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF2B3E3A"/>
                <GradientStop Color="#FF4E6863" Offset="1"/>
                <GradientStop Color="#FF2D574F" Offset="0.815"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="Width" Value="200"/>
    <Setter Property="Height" Value="200"/>
</Style>
<Style TargetType="TextBlock" x:Key="PersonName">
    <Setter Property="FontSize" Value="35" />
    <Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style TargetType="TextBlock" x:Key="PersonCedula">
    <Setter Property="FontSize" Value="25" />
    <Setter Property="HorizontalAlignment" Value="Right"/>
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="Foreground" Value="Gold"/>
    <Setter Property="Margin" Value="0,20,0,20"/>
</Style>

<Style TargetType="TextBlock" x:Key="PersonProfession">
    <Setter Property="FontSize" Value="35" />
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="Foreground" Value="YellowGreen"/>
</Style>

<Style TargetType="TextBlock" x:Key="Encabezado">
    <Setter Property="FontSize" Value="45" />
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="Foreground" Value="White"/>
</Style>


<Style TargetType="Grid">
    <Setter Property="Margin" Value="5,5,0,0"/>
</Style>

Creamos un GridView que sea capaz de mostrar una lista de personas normal sin pensar en grouping, asíque le hacemos Binding con DataSourcePersonas

<GridView ItemsSource="{Binding Source={StaticResource DataSourcePersonas},
                                Path=ListaPersonas}">
    <GridView.ItemTemplate>
        <DataTemplate>
            <StackPanel Style="{StaticResource apptile}">
                <TextBlock Style="{StaticResource PersonName}"       Text="{Binding Nombre}"/>
                <TextBlock Style="{StaticResource PersonName}"       Text="{Binding Apellido}"/>
                <TextBlock Style="{StaticResource PersonCedula}"     Text="{Binding Cedula}"/>
                <TextBlock Style="{StaticResource PersonProfession}" Text="{Binding Profesion}"/>
            </StackPanel>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

De esta forma los datos se visualizan así:

Una vez esta lista la UI debemos cambiar el datasource, lo establecemos en CvsGruposPersonas, y debemos configurar el GridView para utilizar la información de grupos, para ello editamos el template de grupo para el encabezado y para panel de ítems.

<GridView.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <DataTemplate>
                <TextBlock Style="{StaticResource Encabezado}" Text="{Binding Profesion}"/>
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
        <GroupStyle.Panel>
            <ItemsPanelTemplate>
                <VariableSizedWrapGrid Width="auto" Background="Black"
                                        Margin="0,0,30,0"/>
            </ItemsPanelTemplate>
        </GroupStyle.Panel>
    </GroupStyle>
</GridView.GroupStyle>

Lo que hemos hecho acá es crear un envoltorio para cada grupo, ese envoltorio tiene un encabezado en el cual hemos colocado solo un titulo con la profesión, aprovechando esto podemos remover la profesión de nuestro GridView.

A parte del Header hay que establecer el Panel, como no sabemos el tamaño de cada grupo utilizamos un VariableSizedWrapGrid un contenedor que solo puede ser utilizado dentro de otro Control, este control será el que contenga a todos los items de cada grupo.

Con esto queda terminado!
pero no tan rápido vaquero (jeje, sorry)
Que pasa cuando hay grupos de un tamaño diferente, digamos que el segundo grupo tiene solo 3 items, ocurre un efecto indeseable. Dentro de un GridView TODOS los ítems utilizan el tamaño del primer ítem, por ende si nuestro primer grupo tiene 10 items y el segundo 3, el segundo conserva el ancho necesario para albergar 10 y no 3.

De igual forma si el primer grupo fuera de 3 y el segundo de 10, el segundo (y subsecuentes) grupo solo mostraría 3 items y no tendría espacio para mostrar más, ejemplo.

Modificar el template para optimizar la visualización de los grupos

Para solucionar este último problema podemos hacer uso de un control XAML muy importante VirtualizingStackPanel. Este control aparte de muchas otras cosas, hace que el mismo y sus objetos contenidos no puedan ser mas grandes o más chicos que los objetos que contengan, de tal forma que nos soluciona el problema antes mencionado, si sobra espacio lo recorta y falta lo expande.

Pero dónde lo utilizamos?
Olvidándonos del tema Grouping un GridView permite que modifiquemos el panel dentro del cual coloca todos los items, así que modificamos ese panel remplazándolo por el VirtualizingStackPanel.

<GridView.ItemsPanel>
    <ItemsPanelTemplate>
        <VirtualizingStackPanel Background="Black"
                                Orientation="Horizontal"/>
    </ItemsPanelTemplate>
</GridView.ItemsPanel>

Así las cosas ahora mejora la visualización de los items:

Como te das cuenta es un asunto sencillo, pero necesitas saber un conjunto de varias cosas.

Espero que este artículo le sea de ayuda a muchas personas, si tienen dudas no titubeen en postearlas.

chau!

Print Friendly

Follow @JuanKRuiz

  • 3 Comentarios »
  • Publicado en la categoría 'C#, Windows 8, WinRT, XAML'

3 comentarios to “Cómo crear agrupamiento utilizando listas en WinRT? XAML- C# ”


  • Elbrinner Says:
    October 22nd, 2012 at 5:09 am  

    Está genial, puedes subir el código fuente con el XML? Así seria más fácil de probar y jugar con el código.

    gracias

  • Angel Says:
    March 9th, 2013 at 10:56 am  

    Tengo una duda, Cuando intetno crear la Clase DataSourcePersonas me genera error en la linea: public ObservableCollection ListaPersonas { get; set; }, me dice que La clase ObservableCollection requiere un argumento. algo como ObservableColection, cómo lo soluciono ???

  • JuanK Says:
    March 19th, 2013 at 12:29 am  

    por un problema del coloreador de sintaxis no quedo, pero en escencia es que esa clase es generica

Deja un comentario

Redes Sociales

Follow @JuanKRuiz
Answer Questions

Busca en el blog

Artículos Relacionados

  • C# – XAML – Windows 8 SemanticZoom : Cómo hacer zoom sobre un ítem específico
  • Cómo utilizar controles de lista para mostrar colecciones de datos en WinRT? – C# – XAML
  • Mostrar colores de documentos RTF al cargarlos en un RichEditBox – WinRT – C#
  • Acceder a los archivos de instalación en aplicaciones Windows Store
  • Modificar los bytes / pixeles en un BitmapImage utilizando BitmapEncode – C# – WinRT / Windows Store Apps
  • Modificar los bytes / pixeles en un BitmapImage utilizando WriteableBitmap – C# – WinRT / Windows Store Apps
  • Tutorial: Cómo crear una aplicación lectora de RSS en WinRT – Parte 6
  • Artículos Relacionados

  • C# – XAML – Windows 8 SemanticZoom : Cómo hacer zoom sobre un ítem específico
  • Cómo utilizar controles de lista para mostrar colecciones de datos en WinRT? – C# – XAML
  • Mostrar colores de documentos RTF al cargarlos en un RichEditBox – WinRT – C#
  • Acceder a los archivos de instalación en aplicaciones Windows Store
  • Modificar los bytes / pixeles en un BitmapImage utilizando BitmapEncode – C# – WinRT / Windows Store Apps
  • Modificar los bytes / pixeles en un BitmapImage utilizando WriteableBitmap – C# – WinRT / Windows Store Apps
  • Tutorial: Cómo crear una aplicación lectora de RSS en WinRT – Parte 6
  • Nube de Temas

    API - Azure - C# - codigo - Forms - IE - IE9 - Image - imagenes - IT - Microsoft - MVP - Pinned - PowerShell - Proceso - rendimiento - RSS - sistema - Sistemas Operativos - Site - Visual - WCF - Windows - Windows 8 - Windows Store - WinRT - WndProc - WPF - XAML - XNA

    Blogs recomendados

  • VBCodigoPocketPC Espacio para tratar temas de programacion para dispositivos moviles, Pocket PC, Compact Framework, Embbeded Visual Basic, Visual Basic.NET , C# (C Sharp)
  • Róbinson Moscoso Estaré publicando acá cosas sobre tecnologia .NET, situacioines cotidianas de las que voy aprendiendo… sirve como extensión de memoria.
  • .Net C# Blog de Nelsón Venegas
  • Warnov Microsoft Developer Evangelist
  • IT LIfe Blog de mi Hermano que esta en el lado claro: IT
  • Sorey Garcia Una chica del común con la firme intención de no serlo
  • Black Byte videojuegos, modelado y animación 3d
  • Road to IT World Cosas interesantes de IT
  • Marcela Chitiva Un poco de esto… un poco de aquello
  • Surviving the Nigth El mejor blog para aquellos que nos gustan los “internals”
  • Meta

    1. Log in
    2. WordPress

    Ideas de un Conejo is powered by Wordpress. Theme designed by Juan Carlos Ruiz.