Introducción:
Todos los controles de WPF que tienen elementos dentro, es decir, que tengan una propiedad
Items para acceder a los elementos, se pueden clasificar, el problema es que no tienen un
método Sort para hacerlo.
El primer problema es que los elementos de la colección Items
puede ser de diferentes tipos, ya que, como seguramente sabrás, el contenido de los elementos es...
¡cualquier cosa!
Debido a que no todos los elementos son de tipo cadena o de cualquier otro tipo "clasificable",
en WPF se utiliza otra forma de clasificar, y eso se consigue con la colección
SortDescriptionCollection, que contiene elementos del tipo SortDescription
que define o contiene la propiedad por la que debe clasificarse y si se hará en forma ascendente o
descendente.
La forma de "clasificar" los elementos es fácil... bueno, al menos si se sabe cómo hacerlo... je,
je.
Por ejemplo, para clasificar los elementos de un ListBox
llamado lst, podemos hacerlo así:
Me.lst.Items.SortDescriptions.Add( _
New SortDescription("Content", _
ListSortDirection.Ascending))
Es decir, añadimos un nuevo objeto del tipo SortDescription
a la colección SortDesciptions de los elementos del
ListBox. En ese objeto "clasificador" le indicamos qué propiedad queremos usar para
clasificar, en este caso, el contenido de la lista, es decir, la propiedad Content.
Después indicamos el orden de clasificación, que es un valor del tipo ListSortDirection
que está definida en System.ComponentModel.
Una cosa que es importante es que cada vez que queramos volver a clasificar, por ejemplo, de
forma contraria a la anterior, debemos eliminar las "descripciones" de clasificación que ya hubiera.
Esto lo conseguimos llamando al método Clear de esa colección:
Me.lst.Items.SortDescriptions.Clear()
Al hacer esta "limpieza" conseguimos otro objetivo, que es el de dejar la cosa como estaba... es
decir, sin clasificar, por tanto solo debemos usar ese método si en realidad queremos dejarlo sin
clasificar o si vamos a clasificar de forma diferente el contenido de esa lista.
La ventaja de usar esta forma de clasificar es que podemos crear un método que nos sirva para
clasificar cualquier tipo de colección de elementos. Y esto es lo que hace el siguiente método:
Private Sub clasificarLista(ByVal items As ItemCollection, _
ByVal propiedad As String, _
ByVal ascendente As Boolean?)
' Clasificar los elementos según los parámetros
' Eliminar las definiciones de clasificación que hubiera
items.SortDescriptions.Clear()
If ascendente Then
items.SortDescriptions.Add( _
New SortDescription(propiedad, _
ListSortDirection.Ascending))
Else
items.SortDescriptions.Add( _
New SortDescription(propiedad, _
ListSortDirection.Descending))
End If
End Sub
Nota:
Debido a que el último parámetro es de tipo Boolean?, este código solo será válido
para Visual Basic 2008 (o superior). Aunque si quieres usarlo con Visual Basic 2005, solo tendrás
que cambiar el tipo para que sea
Boolean y después hacer las conversiones pertinentes o... (que nadie nos escuche)
usar Option Strict Off (pero que conste que yo no te he dicho que lo hagas).
Este método lo podemos llamar de varias formas, en el segundo parámetro le indicamos la propiedad
por la que queremos clasificar, por ejemplo, en los
ListBox o los ListView podemos usar la propiedad
Content (como vimos en el ejemplo anterior) y si queremos clasificar los elementos
de un menú, usaremos la propiedad Header:
Private Sub mnuClasMenu_Click(ByVal sender As Object, _
ByVal e As RoutedEventArgs) _
Handles mnuClasMenu.Click
clasificarLista(Me.mnuFichero.Items, "Header", Me.chkAscendente.IsChecked)
End Sub
Respecto a esto de clasificar los menús, si cualquiera de los elementos no tiene un "Header"
por el que clasificar, pues... recibiremos un error. Ese será el caso si hay "separadores" (objetos
del tipo <Separator
/>)
Ahí abajo tienes un ejemplo completo de esto que te acabo de contar, y en este caso, está tanto
el código de Visual Basic como el de C# y el correspondiente XAML con el "diseño" de la ventana.
Nota:
En el caso de C#, debes poner el nombre completo de la clase en el código XAML:
<Window
x:Class="EquivalenciasNETyWPF_cs.Window2"
Espero que te sea de utilidad.
Nos vemos.
Guillermo
El código Xaml
<Window x:Class="Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Prueba de clasificar elementos" Height="400" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu IsMainMenu="True" Grid.Row="0">
<MenuItem Name="mnuFichero" Header="_Fichero">
<MenuItem Header="Abrir" />
<MenuItem Header="Guardar" />
<MenuItem Header="Guardar como" />
<MenuItem Header="Mezclar" />
<MenuItem Header="Cerrar" />
</MenuItem>
<MenuItem Header="_Clasificar">
<MenuItem Name="mnuClasMenu" Header="Clasificar el menú de Fichero" />
<MenuItem Header="Clasificar el ListView" Click="btnClasifLW_Click" />
<MenuItem Header="Clasificar el ListBox" Click="btnClasifLst_Click" />
<Separator />
<MenuItem Header="Dejarlos sin clasificar"
Click="btnSinClasificar_Click" />
</MenuItem>
</Menu>
<StackPanel Grid.Row="1">
<ListView Name="lvw" Margin="0,0,8,8" IsSynchronizedWithCurrentItem="True"
HorizontalAlignment="Stretch">
<ListView.View>
<GridView>
<GridViewColumn Header="Probando"/>
</GridView>
</ListView.View>
<ListViewItem Content="Este es un elemento del ListView" />
<ListViewItem Content="Este es otro elemento del ListView" />
<ListViewItem Content="Este es el tercer elemento del ListView" />
<ListViewItem Content="Con este ya van cuatro" />
<ListViewItem Content="Y este es el quinto..." />
</ListView>
<ListBox Name="lst">
<ListBoxItem Content="Hola Mundo"/>
<ListBoxItem Content="Esto es un listBoxItem"/>
<ListBoxItem Content="Este también es un elemento de un ListBox"/>
<ListBoxItem Content="Porque no engraso los ejes..."/>
<ListBoxItem Content="me llaman abandonao..."/>
</ListBox>
<Label />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
Margin="8,8,0,0">
<Button Name="btnClasifLW" Content="Clasificar el ListView"
Margin="0,0,8,0" />
<Button Name="btnClasifLst" Content="Clasificar el ListBox" />
</StackPanel>
<CheckBox Name="chkAscendente" Content="Clasificar de forma ascendente"
IsChecked="True" Margin="8,8,0,0"
ToolTip="Márcalo para clasificar de forma ascendente..."
HorizontalAlignment="Center"/>
<Button Name="btnSinClasificar"
Content="Restaurar los valores sin clasificar"
Margin="0,8,0,0" />
</StackPanel>
</Grid>
</Window>
El código para Visual Basic 2008
Imports System.Windows
Imports System.Windows.Controls
Imports System.ComponentModel
Partial Public Class Window2
Private Sub btnClasifLW_Click(ByVal sender As Object, _
ByVal e As RoutedEventArgs) _
Handles btnClasifLW.Click
' Clasificar el Listview por el contenido de los elementos
clasificarLista(Me.lvw.Items, "Content", Me.chkAscendente.IsChecked)
End Sub
Private Sub btnClasifLst_Click(ByVal sender As Object, _
ByVal e As RoutedEventArgs) _
Handles btnClasifLst.Click
' Clasificar el ListBox por el contenido de los elementos
clasificarLista(Me.lst.Items, "Content", Me.chkAscendente.IsChecked)
End Sub
Private Sub mnuClasMenu_Click(ByVal sender As Object, _
ByVal e As RoutedEventArgs) _
Handles mnuClasMenu.Click
clasificarLista(Me.mnuFichero.Items, "Header", Me.chkAscendente.IsChecked)
End Sub
''' <summary>
''' Clasifica los elementos de la lista indicada
''' </summary>
''' <param name="items">
''' Elementos de la lista que se va a clasificar
''' </param>
''' <param name="propiedad">
''' Nombre la propiedad por la que se va a clasificar
''' </param>
''' <param name="ascendente">
''' Valor verdadero o falso según sea ascendente o no
''' </param>
''' <remarks></remarks>
Private Sub clasificarLista(ByVal items As ItemCollection, _
ByVal propiedad As String, _
ByVal ascendente As Boolean?)
' Clasificar los elementos según los parámetros
' Eliminar las definiciones de clasificación que hubiera
items.SortDescriptions.Clear()
If ascendente Then
items.SortDescriptions.Add( _
New SortDescription( _
propiedad, _
ListSortDirection.Ascending))
Else
items.SortDescriptions.Add( _
New SortDescription( _
propiedad, _
ListSortDirection.Descending))
End If
End Sub
''' <summary>
''' Dejar todos los elementos sin clasificar
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnSinClasificar_Click(ByVal sender As Object, _
ByVal e As RoutedEventArgs) _
Handles btnSinClasificar.Click
Me.lst.Items.SortDescriptions.Clear()
Me.lvw.Items.SortDescriptions.Clear()
Me.mnuFichero.Items.SortDescriptions.Clear()
End Sub
End Class
El código para C#
using System;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
namespace EquivalenciasNETyWPF_cs
{
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
mnuClasMenu.Click +=new RoutedEventHandler(mnuClasMenu_Click);
}
private void btnClasifLW_Click(object sender, RoutedEventArgs e)
{
// Clasificar el Listview por el contenido de los elementos
clasificarLista(this.lvw.Items, "Content",
this.chkAscendente.IsChecked);
}
private void btnClasifLst_Click(object sender, RoutedEventArgs e)
{
// Clasificar el ListBox por el contenido de los elementos
clasificarLista(this.lst.Items, "Content",
this.chkAscendente.IsChecked);
}
private void mnuClasMenu_Click(object sender, RoutedEventArgs e)
{
clasificarLista(this.mnuFichero.Items, "Header",
this.chkAscendente.IsChecked);
}
/// <summary>
/// Clasifica los elementos de la lista indicada
/// </summary>
/// <param name="items">
/// Elementos de la lista que se va a clasificar
/// </param>
/// <param name="propiedad">
/// Nombre la propiedad por la que se va a clasificar
/// </param>
/// <param name="ascendente">
/// Valor verdadero o falso según sea ascendente o no
/// </param>
/// <remarks></remarks>
private void clasificarLista(ItemCollection items,
string propiedad,
bool? ascendente)
{
// Clasificar los elementos según los parámetros
// Eliminar las definiciones de clasificación que hubiera
items.SortDescriptions.Clear();
if (ascendente.Value)
{
items.SortDescriptions.Add(
new SortDescription(
propiedad, ListSortDirection.Ascending));
}
else
{
items.SortDescriptions.Add(
new SortDescription(
propiedad, ListSortDirection.Descending));
}
}
/// <summary>
/// Dejar todos los elementos sin clasificar
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks></remarks>
private void btnSinClasificar_Click(object sender, RoutedEventArgs e)
{
this.lst.Items.SortDescriptions.Clear();
this.lvw.Items.SortDescriptions.Clear();
this.mnuFichero.Items.SortDescriptions.Clear();
}
}
}
Espacios de nombres usados en el código de este artículo:
System.Windows
System.Windows.Controls
System.ComponentModel