Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

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)

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)


Publicado el 24/Ago/2004
Actualizado el 24/Ago/2004
Autor: Guillermo 'guille' Som

 

Muchas veces no sabe uno que título darle a un artículo para indicar el contenido real del mismo, ya que en la mayoría de las ocasiones ese título será el que "atraiga" a la gente... en esta ocasión, mi intención es explicarte cómo trabajar con los elementos que se añaden a un ListBox. Más concretamente cómo se muestran dichos elementos, es decir, qué es lo que se muestra cuando añadimos algo a un ListBox y de paso explicarte el equivalente (o casi) del método ItemData de VB6.

 

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 With

Pero 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 Class

Es 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 Sub

De 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 ListBox

Como 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 Function

Con 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 VB6

Como 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.


Código para Visual Basic.NET (VB.NET)El código para VB .NET

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
 

Código para C Sharp (C#)El código para C#

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;
    }  
}
 

la Luna del Guille o... el Guille que está en la Luna... tanto monta...