Trabajar con
los elementos de un ListBox Cosas que debes saber sobre los elementos de un ListBox (o cómo simular el ItemData de VB6 y otras cosillas) |
Publicado el 24/Ago/2004
|
En Visual Basic 6, era habitual que además de añadir una cadena al ListBox quisiéramos añadir o relacionar también un valor numérico con dicha cadena, en esa versión de VB usábamos el método ItemData para asociar ese valor numérico, de forma que cada cadena contenida en el ListBox pudiese tener asociado un valor numérico (¿cuantas veces lo he repetido?).
Para hacerlo, usábamos un código parecido a este:With List1 .AddItem "elemento 1" .ItemData(.NewIndex) = 1 .AddItem "elemento 2" .ItemData(.NewIndex) = 2 End WithPero en .NET el control ListBox no tiene un método ItemData (ni una propiedad NewIndex que indicaba cual era el último índice añadido al ListBox), tampoco se añaden solamente cadenas, ya que la colección Items lo que acepta son elementos del tipo Object, por tanto podemos añadir cualquier tipo de datos en esa colección, no solo cadenas, como ocurría en el control ListBox de VB6.
Lo que se muestra en un ListBox
En VB6, como los elementos eran de tipo String, no había problemas con lo que se mostraba en el control, ya que lo que se mostraba era precisamente el contenido de las cadenas que se habían añadido, pero debido a que el tipo de datos que se agrega a la colección Items de un ListBox de .NET es del tipo Object, el .NET Framework lo que hace es mostrar lo que devuelva el método ToString de cada objeto, que añadimos una cadena, se muestra el contenido de la cadena, que añadimos valores numéricos, se muestra el contenido de esos números, pero... ¿que pasa si añadimos elementos del tipo Cliente? ¿qué se muestra? fácil, lo que devuelva el método ToString.
El problema es que si no definimos nuestra propia versión del método ToString, el .NET devuelve el nombre completo de la clase, es decir, el espacio de nombres y el nombre de la clase, separados por un punto entre cada elemento.
Por tanto, lo que debemos hacer es redefinir el método ToString de nuestras clases, para que devuelva el valor que nosotros queremos que devuelva; esta es una recomendación que siempre deberíamos tener en cuenta y que siempre deberíamos hacer nada más crear nuestras clases... no solo si la vamos a añadir a un ListBox, ya que ese método ToString se usa bastante por el .NET cuando queremos obtener una representación en formato String de un objeto.
Simular el ItemData de VB6
Bien, dicho esto, vamos ahora a crear una clase que nos permita "simular" el comportamiento de ItemData de los listboxes manejados por VB6.
Como podemos comprobar en el código anterior (incluso si nunca has trabajado con VB6), lo que se hace es añadir una cadena al ListBox y dicha cadena la "asociamos" con un valor entero. Por tanto, podríamos crear una clase que contenga dos campos (o propiedades), una para el valor de la cadena y otro para el valor numérico, por ejemplo:Class LBItem Public Contenido As String Public Valor As Integer End ClassEs decir, creamos una clase a la que podamos asignar una cadena y un entero.
Vamos a añadirle también un constructor que acepte dos parámetros, uno para asignar a la cadena y otro para asignar al valor numérico:Public Sub New(c As String, v As Integer) Contenido = c Valor = v End SubDe esta forma podríamos añadir elementos a un ListBox de esta forma:
ListBox1.Items.Add(New LBItem("Elemento 1", 1)) ListBox1.Items.Add(New LBItem("Elemento 2", 2))El problema es que ese ListBox mostraría algo como esto:
Figura 1, Los objetos añadidos al ListBoxComo puedes comprobar, lo que se muestra es el nombre de la clase, no el contenido que tiene.
Esto es así por lo que te comenté antes: el .NET usa el método ToString para mostrar el contenido de los elementos de un ListBox, por tanto, deberíamos redefinir el método ToString de nuestra clase para que muestre el valor adecuado, en este caso debería mostrarse lo que hayamos asignado a la propiedad (o en este ejemplo campo público) Contenido.
Para lograrlo, redefinamos el método ToString para que quede de esta forma:Public Overrides Function ToString() As String Return Contenido End FunctionCon este cambio, lo que mostrará el ListBox será lo siguiente:
Figura 2, Ahora se muestra correctamente
Como puedes comprobar esta vez se mostrará lo que esperábamos que se mostrara en el ListBox, y de paso hemos creado una clase que nos puede servir para "simular" al ItemData de VB6, pero con muchísima más versatilidad, ya el valor numérico puede ser de cualquier tipo, no solo entero, y ni que decir tiene que no solo nos limitamos a un valor, sino que podemos añadir a esa clase todo lo que necesitemos, además sin preocuparnos de que el ListBox contenga demasiados datos, ya que lo único que se almacena en la colección Items del ListBox son referencias a los nuevos objetos creados.
Recuperar los elementos del ListBox
Ahora viene la segunda parte, ya que no solo es necesario saber cómo se añaden los elementos al ListBox, sino que también debemos saber cómo recuperarlos.
Imagínate que al seleccionar un elemento del ListBox quieres mostrar el contenido en cajas de textos.
En VB6 usaríamos la propiedad List indicándole un índice numérico y así recuperaríamos el contenido del elemento que se encuentre en dicho índice. Pero como en .NET lo que se añade es un objeto, debemos hacer una conversión al tipo de objeto real que contiene:Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles ListBox1.SelectedIndexChanged Dim lbi As LBItem lbi = CType(ListBox1.SelectedItem, LBItem) TextBox1.Text = lbi.Contenido TextBox2.Text = lbi.Valor.ToString End Sub
Debido a que el elemento almacenado en la colección Items es de tipo LBItem, al recuperarlo debemos hacer una conversión (cast), ya que la colección realmente almacena elementos del tipo Object.
Nota:
Si en lugar de añadir objetos del tipo LBItem hubiésemos añadido, por ejemplo datos de tipo String, también deberíamos hacer una conversión de Object a String al recuperar el valor, aunque en el caso de String es fácil ya que simplemente sería necesario usar el método ToString de la propiedad SelectedItem, pero si en lugar de String lo que tenemos son Integer o Double, tendríamos que hacer la conversión al tipo adecuado.
Bueno, espero que te haya quedado claro lo que quería explicar, independientemente del título del artículo.
Te lo resumo para que no te queden dudas:
-Añadir elementos a un ListBox
-Qué hacer para que se muestre correctamente el contenido de objetos definidos por nosotros
-Cómo recuperar los elementos de un LixtBox (particularmente el que está seleccionado)
-Crear una clase que simule el comportamiento de ItemData de VB6Como valor añadido, en el código de ejemplo se comprueba si el elemento seleccionado es del tipo LBItem o no, además de que se añaden elementos de tipos diferentes para que veas un caso real, el aspecto del formulario en tiempo de diseño sería el siguiente:
Figura 3, El formulario de prueba en tiempo de diseño
Espero que te sea de utilidad.
Nos vemos.
Guillermo
P.S.
Aquí abajo tienes el código completo de VB y de C#.
En ese código se hace la comprobación de que el elemento seleccionado sea del tipo LBItem, en caso contrario se usa el método ToString del Selecteditem.
Public Class Form1 Inherits System.Windows.Forms.Form ' ... Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load ListBox1.Items.Add(New LBItem("Elemento 1", 1)) ListBox1.Items.Add(New LBItem("Elemento 2", 2)) ' ' para trabajar con cadenas ListBox1.Items.Add("Cadena 1") ListBox1.Items.Add("Cadena 2") ' ' para trabajar con Integer ListBox1.Items.Add(125) ListBox1.Items.Add(250) End Sub Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles ListBox1.SelectedIndexChanged ' para que valga para cualquier tipo de datos Dim o As Object = ListBox1.SelectedItem ' If TypeOf o Is LBItem Then Dim lbi As LBItem lbi = CType(o, LBItem) TextBox1.Text = lbi.Contenido TextBox2.Text = lbi.Valor.ToString Else TextBox1.Text = ListBox1.SelectedItem.ToString TextBox2.Text = "" End If ' 'Dim lbi As LBItem 'lbi = CType(ListBox1.SelectedItem, LBItem) 'TextBox1.Text = lbi.Contenido 'TextBox2.Text = lbi.Valor.ToString ' ' si el contenido es de tipo String 'TextBox1.Text = ListBox1.SelectedItem.ToString ' ' Si el contenido es de tipo Integer 'Dim n As Integer = CInt(ListBox1.SelectedItem) 'TextBox1.Text = n.ToString 'TextBox1.Text = ListBox1.SelectedItem.ToString End Sub End Class Class LBItem Public Contenido As String Public Valor As Integer ' Public Sub New(ByVal c As String, ByVal v As Integer) Contenido = c Valor = v End Sub ' Public Overrides Function ToString() As String Return Contenido End Function End Class
public class Form1 : System.Windows.Forms.Form { //... // private void Form1_Load(System.Object sender, System.EventArgs e) { ListBox1.Items.Add(new LBItem("Elemento 1", 1)); ListBox1.Items.Add(new LBItem("Elemento 2", 2)); // // para trabajar con cadenas ListBox1.Items.Add("Cadena 1"); ListBox1.Items.Add("Cadena 2"); // // para trabajar con Integer ListBox1.Items.Add(125); ListBox1.Items.Add(250); } // private void ListBox1_SelectedIndexChanged(object sender, System.EventArgs e) { // para que valga para cualquier tipo de datos object o = ListBox1.SelectedItem; // if( o is LBItem ) { LBItem lbi; lbi = (LBItem)o; TextBox1.Text = lbi.Contenido; TextBox2.Text = lbi.Valor.ToString(); } else { TextBox1.Text = ListBox1.SelectedItem.ToString(); TextBox2.Text = ""; } // // LBItem lbi; // lbi = (LBItem)ListBox1.SelectedItem; // TextBox1.Text = lbi.Contenido; // TextBox2.Text = lbi.Valor.ToString(); // // // // si el contenido es de tipo string; // TextBox1.Text = ListBox1.SelectedItem.ToString(); // // // // Si el contenido es de tipo int; // int n = Convert.ToInt32(ListBox1.SelectedItem); // TextBox1.Text = n.ToString(); // TextBox1.Text = ListBox1.SelectedItem.ToString(); } } // class LBItem { public string Contenido; public int Valor; // public LBItem(string c, int v) { Contenido = c; Valor = v; } // public override string ToString() { return Contenido; } }