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:
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;
Cedula
get
return
_cedula; }
set
_cedula = value;
SetProperty(
ref
_cedula, value);
}
string
_nombre;
Nombre
_nombre; }
_nombre = value;
_nombre, value);
_apellido;
Apellido
_apellido; }
{ SetProperty(
_apellido, value); }
_profesion;
Profesion
_profesion; }
_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.
GrupoPersonas
Profesion {
;
; }
List< Persona> ListaPersonas {
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.
DataSourcePersonas
ObservableCollection ListaPersonas {
DataSourcePersonas()
var listaFull = TraerInfoDesdeOrigenDeDatos();
ListaPersonas =
new
ObservableCollection(listaFull);
ObservableCollection ListaPersonasAgrupada {
void
Initialize()
var lista = from persona
in
ListaPersonas
group persona by persona.Profesion into grupo
select
GrupoPersonas()
Profesion = grupo.Key,
ListaPersonas = grupo.ToList()
};
ListaPersonasAgrupada =
ObservableCollection(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.
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
"CvsGruposPersonas"
IsSourceGrouped
"True"
Source="{Binding Source={StaticResource DataSourcePersonas},
Path
ListaPersonasAgrupada
}"
ItemsPath
"ListaPersonas"
</
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:
El CollectionViewSource necesita saber cual de esos atributos contiene las personas de cada grupo de personas, eso es ItemsPath.
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
"apptile"
TargetType
"StackPanel"
Setter
Property
"Margin"
Value
"5"
"Background"
Setter.Value
LinearGradientBrush
EndPoint
"0.5,1"
StartPoint
"0.5,0"
GradientStop
Color
"#FF2B3E3A"
"#FF4E6863"
Offset
"1"
"#FF2D574F"
"0.815"
"Width"
"200"
"Height"
"TextBlock"
"PersonName"
"FontSize"
"35"
"TextWrapping"
"Wrap"
"PersonCedula"
"25"
"HorizontalAlignment"
"Right"
"Foreground"
"Gold"
"0,20,0,20"
"PersonProfession"
"YellowGreen"
"Encabezado"
"45"
"White"
"Grid"
"5,5,0,0"
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},
}">
GridView.ItemTemplate
DataTemplate
StackPanel
"{StaticResource apptile}"
TextBlock
"{StaticResource PersonName}"
Text
"{Binding Nombre}"
"{Binding Apellido}"
"{StaticResource PersonCedula}"
"{Binding Cedula}"
"{StaticResource PersonProfession}"
"{Binding Profesion}"
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
"{StaticResource Encabezado}"
GroupStyle.Panel
ItemsPanelTemplate
VariableSizedWrapGrid
Width
"auto"
Background
"Black"
Margin
"0,0,30,0"
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.
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
VirtualizingStackPanel
Orientation
"Horizontal"
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!