ComboBox MultiColumna en ASP.NET

Hace poco me hizo falta implementar un componente estilo ComboBox que al desplegarse muestre dos columnas.

Por ejemplo, en una tabla de países se pueden almacenar las iniciales del país en un campo y el nombre del país en otro. La idea es que el componente cuando se despliegue muestre dos columnas con estos datos (Columna 1 -> Iniciales del país. Columna 2 -> Nombre completo del país). Como en la siguiente imagen:

ASP.NET no brinda ningún componente con esta característica. Comparto aquí como solucioné este problema para que lo utilice todo el que quiera, agradecería mucho cualquier comentario.

Antes de comenzar a inventar busqué en internet con esperanzas de que existiera algo que solucionara mi problema, lo mejor que encontré fue este post , MAGNÍFICO!!!,solo que no se acomodaba a lo que exactamente yo necesitaba, pero me sirvió de inspiración al ver el uso que Thomas le dió al componente DropDownExtender del AJAX Toolkit. Gracias Thomas.

Primeros pasos.

Lo primero que haremos será prepara nuestro archivo .aspx. Para esto usaremos varios componentes de ASP.NET y un componente del AJAX Toolkit. Si todavía no lo tienes, descargarlo!!!

Instalando el AJAX Toolkit

Para el funcionamiento de nuestro ComboBoxMulticolumna necesitamos usar el DropDownExtender del AJAX Toolkit. Si aún no tienes en el Toolbox los componentes del AJAX Toolkit sigue los siguientes pasos:

– Descarga el último paquete del AJAX Toolkit

– Una vez descargado, descomprímelo.

– Con el Visual Studio abierto en nuestro proyecto muévete hasta el ToolBox. Ahí en algún espacio vacío da clic derecho y selecciona Add Tab, dale un nombre, por ejemplo, AJAX Toolkit.

– En el nuevo Tab que aparece da clic derecho y selecciona Choose ítem. En la ventana que te saldrá da en el botón Browser, muévete hasta la carpeta descomprimida del AJAX Toolkit, entra en la carpeta de ejemplo (AjaxControlToolkitSampleSite), una vez dentro, entra en la carpeta bin y selecciona la dll del AJAX Toolkit. Da clic en OK y listo, al momento te saldrán todos los componentes del AJAX toolkit en el Toolbox del Visual Studio, listos para usarlos en tu proyecto.

Una vez que tenemos todos los componentes del AJAX Toolkit en el Toolbox seleccionamos y arrastramos para nuestro .aspx el componente DropDownExtender.

El Visual Studio nos debe agregar el siguiente código:

En el lugar donde soltamos el componente:


<asp:DropDownExtender ID="DropDownExtender1" runat="server"> </asp:DropDownExtender> 

En la cabecera del archivo (para registrar el AJAX Toolkit)


<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %> 

Y tenemos listo el DropDownExtender ahora nos queda agregar a nuestro .aspx los componentes de ASP.NET que usará el ComboBoxMulticolumna. Para esto agregamos al archivo desde el Toolbox un Panel y un Label (estos los colocamos dentro de un UpdatePanel, agregamos también al UpdatePanel el DropDownExtender que ya tenemos en nuestra página) y un ScriptManager (Este último para que funcione el DropDownExtender).


<asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> 
 
<asp:UpdatePanel ID="UPnlComboBoxMultiColumna" runat="server"> 
<ContentTemplate> 
 
        <asp:Panel ID="PnlComboBoxMultiColumna" runat="server"> 
        </asp:Panel> 
 
        <asp:Label ID="LblComboBoxMultiColumna" runat="server" Text=""></asp:Label> 
 
        <asp:DropDownExtender ID="DDEComboBoxMultiColumna" runat="server"> 
        </asp:DropDownExtender> 
 
</ContentTemplate>  
</asp:UpdatePanel> 

Ahora vamos a indicarle al DropDownExtender los dos componentes que usará para su funcionamiento. (Un Label que funciona como base del ComboBox y un Panel que contiene todos los ítems que se muestran al desplegarse el ComboBox) Para esto le agregamos al DropDownExtender los siguientes pares de atributo – valor:

TargetControlID="LblComboBoxMultiColumna"
DropDownControlID="PnlComboBoxMultiColumna"

Ahora el DropDownExtender quedará de la siguiente forma:


<asp:DropDownExtender ID="DDEComboBoxMultiColumna" runat="server" TargetControlID="LblComboBoxMultiColumna" DropDownControlID="PnlComboBoxMultiColumna"> 
</asp:DropDownExtender> 
 

También debemos indicarle algunas reglas de estilo al Panel para que aparezca inicialmente oculto.


<asp:Panel ID="PnlComboBoxMultiColumna" runat="server" Style="display: none; visibility: hidden;"></asp:Panel> 
 

Vamos a dejar momentáneamente el aspx aquí y vamos a pasar al aspx.cs

Implementación del aspx.cs (código en el servidor)

Lo primero que haremos será preparar la fuente de datos para poblar el ComboBoxMultiColumna. Lo más probable es que la fuente de datos de este componente sea una tabla de la base de datos de nuestra aplicación pero para el ejemplo usaremos una lista de datos sencilla.

Vamos a crear la clase para almacenar los datos.

public class Country
{
    public string id { get; set; }
    public string value { get; set; }

    public Country(string id, string value)
    {
        this.id = id;
        this.value = value;
    }
}

Esta es un clase muy sencilla que nos servirá para almacenar id y valor de cada País. El ComboBoxMultiColumna lo que mostrará será: en la primera columna el id del país y en la segunda columna el valor (nombre del país).

Bien, teniendo esto, en el Page_Load de nuestro .aspx.cs agregamos el siguiente código

List<Country> list = this.getDataSource();

foreach (Country country in list)
{
    if (LblComboBoxMultiColumna.Text == "")
    {
        LblComboBoxMultiColumna.Text = country.id;
    }

    LinkButton lb = new LinkButton();
    lb.ID = "Lbl" + country.id + "ComboBoxMultiColumna";

    lb.Text = "<div>";
    lb.Text += "<span style="width: 40px;">" + 'u00A0' + country.id + 'u00A0' + "</span>";
    lb.Text += "" + 'u00A0' + country.value + 'u00A0' + "";
    lb.Text += "</div>";

    PnlComboBoxMultiColumna.Controls.Add(lb);
}

Explico detalladamente que estamos haciendo aquí. El objetivo de estas líneas es poblar el ComboBoxMultiColumna. Para esto, primero obtenemos el listado de países que funcionará como fuente de datos para nuestro ComboBoxMultiColumna. Seguidamente está el código del método getDataSource()

private List<Country> getDataSource()
{
    List<Country> list = new List();

    list.Add(new Country("ABW","Aruba"));
    list.Add(new Country("AFG","Afghanistan"));
    list.Add(new Country("AGO","Angola"));
    list.Add(new Country("BHS","Bahamas"));
    list.Add(new Country("BMU","Bermudas"));
    list.Add(new Country("BOL","Bolivia"));
    list.Add(new Country("BRA","Brazil"));
    list.Add(new Country("COG","Congo"));
    list.Add(new Country("COL","Colombia"));
    list.Add(new Country("CUB","Cuba"));
    list.Add(new Country("CYM","Cayman"));
    list.Add(new Country("EGY","Egypt"));
    list.Add(new Country("EST","Estonia"));

    return list;
}

Después de obtener los países, recorremos el listado con el objetivo de crear un LinkButton con los datos del país (id y valor). Este es el punto clave de nuestro ComboBoxMultiColumna. Lo que haremos será obtener id y valor y colocarlos cada uno dentro de un que estos a su vez estarán dentro de un

y este será el valor del atributo Text del LinkButton. El if que está al comenzar el foreach su objetivo es configurar el valor que se ve en el ComboBoxMultiColumna la primera vez que carga la página.

Por último, una vez configurado nuestro LinkButton lo agregamos al Panel que se despliega al hacer clic sobre el Labelsimulando el comportamiento de un ComboBoxque no está desplegado, gracias al DropDownExtender.

Vamos a pararnos aquí y ver cómo está quedando todo. Si ejecutamos nuestra página en el navegador veremos que nos sale un Label con el ID del primer elemento que agregamos a nuestra fuente de datos (ABW) si damos clic sobre este elemento aparece el resto de los elementos … pero … pasa algo …. Todos los elementos aparecen uno sobre otro por lo que no se entiende nada :-S. No hay problema, vamos a arreglar este lío agregando un poco de estilo a nuestros elementos.

Vamos a nuestro archivo aspx y agregamos los siguientes estilos.


    <style type="text/css">
        
     .MultiColumnTextBoxStyle
        {            
            border: 1px solid #99bbe8;
	    padding: 1px 1px 0px 3px;	        
	    font-size:12px;  
            font-family:"Calibri","sans-serif"; 
            background-color:#ffffe1;    
        }
     
        .MultiColumnContextMenuPanel 
        {
            height:150px;            
            overflow:scroll;            
            overflow-x: hidden;
	    border: 1px solid #868686;
	    z-index: 1000;
	    background: url(menu-bg.gif) repeat-y 0 0 #FAFAFA;
	    background-color:#FFF;	        
	    cursor: default;
	    padding: 1px 1px 0px 1px;
	    font-size:12px; 
            font-family:"Calibri","sans-serif"; 
        }
 
        a.MultiColumnContextMenuItem
        {            
	    margin: 1px 0 1px 0;
	    display: block;
	    color: #003399;
	    text-decoration: none;
	    cursor: pointer;	
	    padding: 4px 19px 4px 4px;
	    white-space: nowrap;
        }

        a.MultiColumnContextMenuItem-Selected
        {
	    font-weight: bold;
        }

        a.MultiColumnContextMenuItem:hover
        {
	    background-color: #FFE6A0;
	    color: #003399;
	    border: 1px solid #D2B47A;
	    padding: 3px 18px 3px 3px;
	    text-decoration:none;    
        }
        
    </style>

Notar que MultiColumnContextMenuPanel usa una imagen de fondo. Esta imagen la podemos obtener de la carpeta AjaxControlToolkitSampleSiteApp_ThemesSampleSiteThemeImages del AJAX Toolkit.

IMPORTANTE: Debemos cambiarle el tamaño agrandándola a 40 pixeles de largo y mantener la altura igual, para que ocupe todo el espacio de la primera columna del ComboBoxMultiColumna.

Ya con las reglas de estilo en nuestro aspx (o css) vamos a indicar los atributos CssClass a cada componente. Quedarán de la siguiente forma:


<asp:Panel ID="PnlComboBoxMultiColumna" runat="server"  Width="200px" CssClass="MultiColumnContextMenuPanel" Style="display: none; visibility: hidden;"></asp:Panel> 
 
<asp:Label ID="LblComboBoxMultiColumna" runat="server" Text="" Width="65px" CssClass="MultiColumnTextBoxStyle"></asp:Label> 

Si vemos ahora nuestra página en el navegador notaremos el cambio. Ya tenemos un ComboBoxMultiColumna. 😉

Pero faltan algunos detalles. Si pasamos el mouse sobre cualquier ítem del ComboBoxMultiColumna no hay ningún cambio visual, pues para solucionar esto ya tenemos una regla CSS definida solo debemos agregársela a cada uno de los LinkButton cuando los creamos. Para esto vamos al aspx.cs y en el bloque de código dentro del foreach donde creamos los LinkButton agregamos la siguiente línea

lb.CssClass = "MultiColumnContextMenuItem";

Ahora volvemos a ejecutar nuestra aplicación en el navegador y veremos el cambio. Mucha mejor verdad 😉

Bien, ya tenemos casi todo el trabajo terminado pero … ¿Qué falta? … pues lo más importante, hasta ahora todo muy bonito pero si damos clic en cualquier Ítem de los que se despliegan en el ComboBoxMultiColumna no obtenemos ningún resultado. Bien, pues para esto haremos lo siguiente:

Cuando seleccionemos un Ítem del ComboBoxMultiColumna actualizaremos un trozo de nuestra página mediante un UpdatePanel mostrando el elemento seleccionado. Y también actualizaremos el elemento que se muestra cuando el ComboBoxMultiColumna no está desplegado.

Para esto vamos a agregar a nuestro archivo aspx un Label dentro del UpdatePanel así debe quedar:


        <asp:UpdatePanel ID="UPnlComboBoxMultiColumna" runat="server"> 
            <ContentTemplate> 
 
                <asp:Panel ID="PnlComboBoxMultiColumna" runat="server" Width="200px" CssClass="MultiColumnContextMenuPanel" Style="display: none; visibility: hidden;"> 
                </asp:Panel> 
 
                <asp:Label ID="LblComboBoxMultiColumna" runat="server" Text="" Width="65px" CssClass="MultiColumnTextBoxStyle"></asp:Label> 
 
                <asp:DropDownExtender ID="DDEComboBoxMultiColumna" runat="server" TargetControlID="LblComboBoxMultiColumna" DropDownControlID="PnlComboBoxMultiColumna"> 
                </asp:DropDownExtender> 
                 
                <asp:Label ID="LblSelectedItemComboBoxMultiColumna" runat="server" Text=""></asp:Label> 
             
            </ContentTemplate>  
        </asp:UpdatePanel>   

Ya teniendo listo el aspx pasamos a nuestro aspx.cs para agregar algunos nuevos detalles.

Primero, vamos a configurar el evento Clic de cada LinkButton (Ítem del ComboBoxMultiColumna) para que cuando lo seleccionemos se dispare la acción que deseamos. Para esto en el bloque de código donde creamos cada LinkButton dentro del foreach agregamos la siguiente línea

lb.Click += new EventHandler(ComboBoxMultiColumnaItem_onClick);

Evidentemente tenemos también que crear el método ComboBoxMultiColumnaItem_onClick. Aquí está

    protected void ComboBoxMultiColumnaItem_onClick(object sender, EventArgs e)
    {
        string linkButtonText = ((LinkButton)sender).Text;
        string ID = linkButtonText.Split('u00A0')[1];
        string VALUE = linkButtonText.Split('u00A0')[3];
        LblComboBoxMultiColumna.Text = ID;
        LblSelectedItemComboBoxMultiColumna.Text = " Elemento seleccionado: " + VALUE;
    }

Si nos fijamos en la línea donde configuramos el atributo Text de cada LinkButton al principio y al final de cada dato (id y valor) colocamos un carácter invisible (u00A0). Esto lo hacemos con el objetivo de tener un carácter para picar el Text y poder separar cada componente (id y valor) sin afectar visualmente los Ítems del ComboBoxMultiColumna.

Bien, pues entonces lo que hacemos en el método que se dispara con el onClick de cada LinkButton es eso,separamos ese string mediante el carácter vacío y tomamos id y valor. Una vez que tenemos estos podemos hacer con ello lo que queramos. En nuestro caso lo que hacemos es mostrar el valor en un Label y actualizar el ID seleccionado.

Nos faltó un pequeño detalle. Cuando abrimos la página por primera vez el ComboBoxMultiColumna nos muestra el id del primer Ítem pero el Label que nos indica el elemento seleccionado no muestra nada. Para arreglar este pequeño detalle en nuestro archivo aspx.cs dentro del foreach, dentro del if agregamos la siguiente línea:

LblSelectedItemComboBoxMultiColumna.Text = " Elemento seleccionado: " + country.value;

Vamos hacia el navegador para ver que hemos logrado…. EEEHHHHH ¡!!!! Ya tenemos un ComboBoxMultiColumna completamente funcional ;-).

Espero que le sea de ayuda a alguien, agradecería cualquier comentario. A continuación todo el código del ejemplo para una referencia más rápida.

Archivo aspx


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Culture="es-ES"%>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<%--
// ----------------------------------------------------------
//  Creado por Fernando Castillo Coello el 04/07/2011 
// ----------------------------------------------------------
//  http://nan2cc.wordpress.com 
//  nan2castillocoello@gmail.com
// ----------------------------------------------------------
//  Puede usar y modificar este código sin ninguna limitación
// ----------------------------------------------------------
--%>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>

    <style type="text/css">
        
     .MultiColumnTextBoxStyle
        {            
            border: 1px solid #99bbe8;
	        padding: 1px 1px 0px 3px;	        
	        font-size:12px;  
            font-family:"Calibri","sans-serif"; 
            background-color:#ffffe1;    
        }
     
        .MultiColumnContextMenuPanel 
        {
            height:150px;            
            overflow:scroll;            
            overflow-x: hidden;
	        border: 1px solid #868686;
	        z-index: 1000;
	        background: url(menu-bg.gif) repeat-y 0 0 #FAFAFA;
	        background-color:#FFF;	        
	        cursor: default;
	        padding: 1px 1px 0px 1px;
	        font-size:12px; 
            font-family:"Calibri","sans-serif"; 
        }
 
        a.MultiColumnContextMenuItem
        {            
	        margin: 1px 0 1px 0;
	        display: block;
	        color: #003399;
	        text-decoration: none;
	        cursor: pointer;	
	        padding: 4px 19px 4px 4px;
	        white-space: nowrap;
        }

        a.MultiColumnContextMenuItem-Selected
        {
	        font-weight: bold;
        }

        a.MultiColumnContextMenuItem:hover
        {
	        background-color: #FFE6A0;
	        color: #003399;
	        border: 1px solid #D2B47A;
	        padding: 3px 18px 3px 3px;
	        text-decoration:none;
	        
        }
        
    </style>

</head>
<body>
    <form id="form1" runat="server">
    <div>

        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>

        <asp:UpdatePanel ID="UPnlComboBoxMultiColumna" runat="server">
            <ContentTemplate>

                <asp:Panel ID="PnlComboBoxMultiColumna" runat="server"  Width="200px" CssClass="MultiColumnContextMenuPanel"
                Style="display: none; visibility: hidden;">
                </asp:Panel>

                <asp:Label ID="LblComboBoxMultiColumna" runat="server" Text="" Width="65px" CssClass="MultiColumnTextBoxStyle"></asp:Label>

                <asp:DropDownExtender ID="DDEComboBoxMultiColumna" runat="server"  
                TargetControlID="LblComboBoxMultiColumna" DropDownControlID="PnlComboBoxMultiColumna">
                </asp:DropDownExtender>
                
                <asp:Label ID="LblSelectedItemComboBoxMultiColumna" runat="server" Text=""></asp:Label>
            
            </ContentTemplate> 
        </asp:UpdatePanel>

    </div>
    </form>
</body>
</html>


Archivo aspx.cs


// ----------------------------------------------------------
//  Creado por Fernando Castillo Coello el 04/07/2011 
// ----------------------------------------------------------
//  http://nan2cc.wordpress.com 
//  nan2castillocoello@gmail.com
// ----------------------------------------------------------
//  Puede usar y modificar este código sin ninguna limitación
// ----------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

/// <summary>
/// Clase para almacenar los valores de cada Items del ComboBoxMultiColumna
/// </summary>
public class Country
{
    public string id { get; set; }
    public string value { get; set; }

    public Country(string id, string value)
    {
        this.id = id;
        this.value = value;
    }
}

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        List<Country> list = this.getDataSource();

        foreach (Country country in list)
        {
            if (LblComboBoxMultiColumna.Text == "")
            {
                LblComboBoxMultiColumna.Text = country.id;
                LblSelectedItemComboBoxMultiColumna.Text = " Elemento seleccionado: " + country.value;
            }

            LinkButton lb = new LinkButton();
            lb.ID = "Lbl" + country.id + "ComboBoxMultiColumna";
            lb.CssClass = "MultiColumnContextMenuItem";
            lb.Click += new EventHandler(ComboBoxMultiColumnaItem_onClick);

            lb.Text = "<div style=" height:15px; ">";
            lb.Text += "<span style="width:40px; float:left; margin:0px; padding:0px;">" + 'u00A0' + country.id + 'u00A0' + "</span>";
            lb.Text += "<span>" + 'u00A0' + country.value + 'u00A0' + "</span>";
            lb.Text += "</div>";

            PnlComboBoxMultiColumna.Controls.Add(lb);
        }
    }

    /// <summary>
    /// Se dispara con el onClick de cada Item del ComboBoxMultiColumna.
    /// </summary>
    protected void ComboBoxMultiColumnaItem_onClick(object sender, EventArgs e)
    {
        string linkButtonText = ((LinkButton)sender).Text;
        string ID = linkButtonText.Split('u00A0')[1];
        string VALUE = linkButtonText.Split('u00A0')[3];
        LblComboBoxMultiColumna.Text = ID;
        LblSelectedItemComboBoxMultiColumna.Text = " Elemento seleccionado: " + VALUE;
    }

    /// <summary>
    /// Retorna el listado de paises que funcionará como fuente de datos para el ComboBoxMultiColumna.
    /// </summary>
    private List<Country> getDataSource()
    {
        List<Country> list = new List<Country>();

        list.Add(new Country("ABW", "Aruba"));
        list.Add(new Country("AFG", "Afghanistan"));
        list.Add(new Country("AGO", "Angola"));
        list.Add(new Country("BHS", "Bahamas"));
        list.Add(new Country("BMU", "Bermudas"));
        list.Add(new Country("BOL", "Bolivia"));
        list.Add(new Country("BRA", "Brazil"));
        list.Add(new Country("COG", "Congo"));
        list.Add(new Country("COL", "Colombia"));
        list.Add(new Country("CUB", "Cuba"));
        list.Add(new Country("CYM", "Cayman"));
        list.Add(new Country("EGY", "Egypt"));
        list.Add(new Country("EST", "Estonia"));

        return list;
    }
}


Fernando Castillo Coello
Follow me

Fernando Castillo Coello

Gameplay Programmer at GameOlic
I've been into games development with Unreal Engine since 2014 and want to share with you some of the tips and tricks that I learned during my journey.
Fernando Castillo Coello
Follow me

4 pensamientos en “ComboBox MultiColumna en ASP.NET

    1. nan2cc

      Hola Txema, si se puede hacer con iconos sin ningún problema. Si te fijas en el Page_Load donde se recorre el listado de datos para poblar el componente, cada ítem esta compuesto por dos span uno para cada columna. Es este punto podemos agregar lo que queramos. Ya sea agregar otra columna con los iconos o la primera columna usarla para los iconos.
      Gracias por el comentario.

      Responder

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>