XBindingList: Un BindingList con esteroides
Una de las clases del .NET Framework que utilizo con más frecuencia es BindingList.
Como saben, BindingList es una colección genérica optimizada para servir como origen de datos para controles como el DataGridView y otros.
Es de gran utilidad, pero no están implementadas 3 características deseables: Búsqueda, Ordenación y Filtrado.
Mostraré la forma de crear una clase derivada de BindingList que implemente estas prestaciones. Como es un tema mas o menos extenso, irá desglosado en varios posts.
A falta de un mejor nombre, llamaré a esta clase XBindingList.
Definamos primero una clase Persona (ID, Nombre, Fecha de Nacimiento) que usaremos para nuestras pruebas.
public class Persona
{
private int _id;
private string _nombre;
private DateTime _fechaNacimiento;
public int Id
{
get { return _id; }
set { _id = value; }
}
public string Nombre
{
get { return _nombre; }
set { _nombre = value; }
}
public DateTime FechaNacimiento
{
get { return _fechaNacimiento; }
set { _fechaNacimiento = value; }
}
public Persona()
{
}
public Persona(int id, string nombre, DateTime fechaNacimiento)
:this()
{
this.Id = id;
this.Nombre = nombre;
this.FechaNacimiento = fechaNacimiento;
}
}
Ahora si estamos listos para entrar en materia. Empecemos definiendo nuestra clase XBindingList:
public class XBindingList<T> : BindingList<T>
{
//...
}
Simplemente hemos declarado la clase XBindingList como una nueva colección genérica que se deriva de BindingList.
Ahora suministraremos la primera dosis de esteroides a nuestra nueva clase XBindingList: La capacidad de realizar búsquedas.
Se desea buscar algún valor en cualquiera de las propiedades de nuestra clase. Es decir, si usamos la clase Persona, debemos ser capaces de buscar un elemento ya sea por el Id, el Nombre o la Fecha de Nacimiento.
En general, para implementar nuevas características en una clase derivada de BindingList, debemos solapar (override) ciertas propiedades y métodos. Para las operaciones de búsqueda debemos solapar la propiedad SupportSearchingCore y el método FindCore. SuportsSearchingCore se encarga de comunicar al mundo si la colección es capaz o no de realizar búsquedas, mientras que FindCore se encarga de realizar la búsqueda propiamente dicha.
Primero vamos a enseñarle a XBindingList que responda afirmativamente cuando alguien le pregunte si es capaz de realizar búsquedas. Para ello solapamos la propiedad SupportsSearchingCore, asi:
protected override bool SupportsSearchingCore
{
get
{
return true;
}
}
Ahora viene el trabajo real: implementar el mecanismo de búsqueda.
El método FindCore recibe dos parámetros: prop que le indica en cuál propiedad debe buscar el valor, y key que es el valor que se debe buscar. Como punto de partida implementaremos un mecanismo de búsqueda secuencial. Es muy sencillo de codificar, pero es ineficiente cuando la colección es muy grande.
protected override int FindCore(PropertyDescriptor prop, object key)
{
foreach (T item in this)
{
if (prop.GetValue(item).ToString().Equals(key.ToString()))
{
return this.IndexOf(item);
}
}
return -1;
}
Esta es la idea básica, tal cual está implementado el algoritmo de búsqueda, solamente encuentra el primer elemento que coincida exactamente con la el valor de key. Sin duda se pueden añadir muchas mejoras como comparaciones parciales, buscar siguiente, etc. En algún momento postearé un algoritmo mejorado, por mientras los animo a que en algún comentario puedan compartir conmigo (y los otros lectores del blog) sus propios algoritmos.
Nada más, XBindingList ahora soporta búsquedas, veamos un ejemplo de su utilización.
Incluimos un BindingSource, un BindingNavigator y un DataGridView. En la barra de herramientas del BindingNavigator incluimos un Combobox (cbPropiedad), un TextBox (tbValor) y un Button (btnBuscar). Agregamos en cbPropiedad los nombres de las propiedades es que queremos poder buscar. En tbValor el usuario escribirá el valor que quiere buscar y la búsqueda se realizará al hacer click en el botón btnBuscar.
En el constructor del Form declaramos una variable llamada lista que es una instancia de XBindingList que almacenará elementos del tipo Persona, la poblamos con unos pocos datos y finalmente hacemos que nuestro BindingSource use lista como su origen de datos.
public FormTest2()
{
InitializeComponent();
XBindingList<Persona> lista = new XBindingList<Persona>();
lista.Add(new Persona(1, "Juan Perez", new DateTime(1980, 12, 10)));
lista.Add(new Persona(2, "Maria Fernandez", new DateTime(1975, 12, 7)));
lista.Add(new Persona(3, "Oscar Luna", new DateTime(1989, 4, 1)));
personaBindingSource.DataSource = lista;
}
Asi que el único código que nos queda por escribir es precisamente el manejador del evento Click del botón. Es tan simple como esto:
private void btnBuscar_Click(object sender, EventArgs e)
{
int idx =personaBindingSource.Find(cbPropiedad.Text, tbValor.Text);
if (idx >= 0)
personaBindingSource.Position = idx;
}
Eso es todo, ahora nuestra recién creada XBindingList soporta búsquedas.
Siguiente episodio: XBindingList se vuelve ordenada.
2 comentarios:
Que buen articulo marcos... estoy tratando de cambiar una clase (entidad de datos con DataSet Tipados a) a BindingList, necesito ciertos calculos que no me lo esta permitiendo el dataset tipado y deseo que formen parte de la logica de negocio (no quiero hacerlo en el formulario).
Y para cuando tenenos la continuación del articulo...
Saludos...
Hola, gracias por el comentario.
Quiero disculparme contigo (y con todos los lectores) por no estar publicando ultimamente. Pasa que entre el trabajo, la iglesia y ahora los preparativos para mi matrimonio (que es en dos semanas) :D he estado demasiado ocupado.
Asi que les pido me tengan un poco más de paciencia por estas dos semanas, y luego publicaré los artículos que prometí y otros igual de interesantes que ya tengo en el tintero.
Quedó pendiente, por ejemplo, el añadir la funcionalidad de filtrado al BindingList, que ya lo tengo casi listo, será el primero q publique.
Gracias otra vez, saludos.
Publicar un comentario