Ir a la sección de Como en .NET Cómo en .NET
 

Array de controles en C# 2.0

En realidad es una colección generic de controles

 

Publicado el 09/Abr/2007
Actualizado el 09/Abr/2007
Autor: Guillermo 'guille' Som

En este artículo te explico cómo crear un array (en realidad una colección generic) de controles con Visual C# 2005 (o C# 2.0 o superior), para usarlo en tus formularios. Pulsa aquí para ver cómo crear el array de controles con Visual Basic 2005. (o Visual Basic 8.0 o superior)

 

 

Introducción:

Esta es la versión para Visual Basic 2005 del código que publiqué el 14 de Noviembre de 2002 para crear un array de controles en .NET (con ejemplos para Visual Basic y Visual C#).

En esta ocasión la clase que hace las veces de array (ArrayControles<T>) está definida como una colección generic de tipo List<T>, de forma que T sea el tipo de control que va a mantener la colección.

Al ser una colección de tipo List<T>, se podrá usar como un array, así que no habrá problemas, pero para facilitar las cosas, la clase/colección también define una sobrecarga de la propiedad predeterminada (indizador en C#) para que se pueda acceder al control por medio del nombre.

 

¿Cómo usar la clase ArrayControles<T>?

Esta clase necesita unos requisitos previos para poder usarla con los controles de un formulario.
Y es que todos los controles del formulario que vayan a estar agrupados en la misma colección, deben llamarse de la misma forma y solo cambiar en el número que habrá que indicar después de un guión bajo.
Por ejemplo, si quieres crear un array de controles TextBox, de forma que puedas acceder a ellos mediante la clase ArrayControles como si fueran un array, es decir, indicando un índice numérico, tendrás que añadir esos controles al formulario y darles nombres que tengan el formato: nombre_indice.
nombre será el nombre que quieras, el guión bajo es el que separa el índice del nombre y por último, un valor numérico que debería coincidir con el índice a usar, es decir, si vas a crear varios controles, procura que los valores de los índices sean consecutivos, y empezando por cero.

Por ejemplo, puedes tener 3 controles de tipo TextBox con estos nombres:
datos_00, datos_01 y datos_02.
(El nombre que le des no es importante, solo que todos empiecen con el mismo nombre.)
Tampoco es importante que uses ceros extras, ya que eso solo será para que tu lo tengas más claro si tienes más de 10 controles, ya que la clase solo tiene en cuenta el valor numérico, por tanto _00 será 0, _01 será 1 y así sucesivamente.
Ese nombre (datos en este ejemplo) será el que tendrás que indicar al crear la instancia de la clase, y al definir la variable de la clase tendrás que decir que tipo de control va a contener:

private ArrayControles<TextBox> m_TextBox1;

...

m_TextBox1 = new ArrayControles<TextBox>("datos", this);

Como ves, en el constructor también puedes indicar el contenedor donde están esos controles, y aquí es donde entra la "importancia" del nombre, ya que en esa colección solo se guardarán los controles que tengan el nombre que has indicado, y que, por supuesto, deben ser del tipo que la colección va a contener, por tanto, no debes darle ese mismo nombre a otros controles de otro tipo o los controles que no quieras que estén en esa misma colección.

Si quieres tener varias colecciones de varios tipos de controles, puedes hacerlo, pero teniendo en cuenta lo que te acabo de comentar: que el nombre de esos controles sean diferentes y que usen el formato "nombre" "guión bajo" "índice numérico".

 

Un ejemplo de cómo usar la colección

En el formulario de prueba de esta clase, he creado tres colecciones para tres tipos de controles, las cuales están definidas de esta forma:

private ArrayControles<Label> m_Label1;
private ArrayControles<TextBox> m_TextBox1;
private ArrayControles<RadioButton> m_RadioButton1;

En el evento Load del formulario es donde se crean las instancias y se asignan los controles:

// Asignar los controles y reorganizar los índices
// No importa si el nombre no coincide exactamente,
// ya que no se diferencian las mayúsculas de las minúsculas.

m_Label1 = new ArrayControles<Label>("Label1", this);
m_TextBox1 = new ArrayControles<TextBox>("textBox1", this);
m_RadioButton1 = new ArrayControles<RadioButton>("RadioButton1", this);

Como ves en el comentario del código anterior, el nombre no tiene porqué coincidir exactamente (no diferencia mayúsculas de minúsculas), ya que en realidad, en el constructor de la clase, el nombre indicado se guarda en minúsculas y en las comprobaciones que se hacen no se hace distinción en el "case" del nombre.

Lo que si es importante es que en el formulario haya controles de tipo Label que tengan el nombre al estilo del indicado en el constructor, en este ejemplo, esos controles deberían llamarse Label1_n, donde _n será un valor numérico que empieza por cero y sea ascendente de uno en uno.

Si el orden no es correlativo y no empiezan por cero, no podrás usar de forma "correcta" el acceso a los datos por el índice numérico, pero si por el nombre.

Por ejemplo, si has definido las etiquetas en el formulario con los nombres Label1_0, Label1_20, Label1_3, al usar m_Label1[1] te devolverá (posiblemente) el control Label1_20, pero si usas: m_Label1["Label1_3"] te devolverá el índice en el que esté el control llamado Label1_3, que "posiblemente" será el índice 2.

Así que... para no complicarte la vida, usa los valores de forma correlativa y empezando por cero.

 

Asignar eventos para los controles de una misma colección

La "gracia" de usar esta colección es que puedes crear un solo método de evento para manejar todos los controles de un mismo array (o colección).

Por ejemplo, si quieres que cuando las cajas de texto reciban el foco se seleccione todo el texto que hay, puedes hacer esto:

private void TextBox1_Enter(object sender, EventArgs e)
{
    m_TextBox1[sender].SelectAll();
}

Ese caso caso, el control que produce el evento debe ser del tipo de la colección. No importa que no esté en el array, al menos tal y como tengo actualmente el código, ya que si cambias el código de la propiedad predeterminada (indizador en C#) que recibe un parámetro de tipo Object usando lo que ahora mismo está comentado, entonces se devolverá un valor nulo si ese control no está en la colección.

Nota:
Ver el código del indizador cuando recibe un parámetro de tipo object, ya que tendrá preferencia sobre el tipo entero, por tanto hay que hacer una comprobación extra para saber si el tipo de datos del parámetro es de tipo entero, de forma que se llame al indizador de la clase base (que es el que usa el parámetro de tipo entero).
Cuando se usa la sobrecarga del tipo string no hay problemas.

En Visual Basic no existe ese efecto no deseado.

Pero si lo que quieres es que, por ejemplo, cuando el TextBox reciba el foco se tenga en cuenta otro control de otro array que tenga el mismo índice, por ejemplo una etiqueta, puedes usar el método Index para averiguar el índice numérico del control, de forma que ese valor lo puedas usar en el otro array (por eso te comentaba que es muy importante que los índices que uses sean correlativos y empiecen por cero, para que no haya problemas, digamos, colaterales).

// Fíjate que en el Label, los valores son _0, _1 y _2
// y en el TextBox son _00, _01 y _02,
// aunque parece que "no coinciden",
// lo que importa es que el valor numérico sea el adecuado.

int index = m_TextBox1.Index(sender);
m_TextBox1[index].SelectAll();
// resaltar la etiqueta
m_Label1[index].BackColor = Color.FromKnownColor(KnownColor.ControlDark);

Al usar el método Index que recibe un tipo Object, el valor del índice que se devuelve es el que se indique en el nombre del control, por tanto, si el TextBox que ha producido el evento en el que está ese código se llama TextBox1_22, el valor de index será 22. Ves nuevamente porqué es importante que los controles estén correlativos y empiecen por cero... (sí, ya se que te has enterado, pero para que quede claro).

 

Y para terminar con la explicación, decirte cómo hacer que todos los controles de una colección usen el mismo método de evento.

Por ejemplo, que todos los controles del array m_TextBox1 usen el evento TextBox1_Enter cuando reciban el foco:

foreach (TextBox txt in m_TextBox1)
{
    txt.Enter += new EventHandler(TextBox1_Enter);
}

Lo mismo tendrías que hacer para el resto de evento, por ejemplo, para que se asigne el evento cuando pulsas una tecla en el control de tipo TextBox:

foreach (TextBox txt in m_TextBox1)
{
    txt.KeyPress += new KeyPressEventHandler(TextBox1_KeyPress);
}

Como puedes suponer, puedes poner todo el código en el mismo bucle, (en el formulario de ejemplo he creado un método que asigna todos los eventos de los tres arrays de controles).

Nota:
En C# 2.0 (o superior) los eventos se pueden asignar sin necesidad de indicar el delegado que deben usar, aunque esos métodos deben tener la firma adecuada o al menos "aceptable" para esos eventos.

Por tanto, los dos bucles anteriores los puedes simplificar de esta forma:

foreach (TextBox txt in m_TextBox1)
{
    txt.Enter += TextBox1_Enter;
    txt.KeyPress += TextBox1_KeyPress;
}

 

Además, tanto en el método TextBox1_Enter como TextBox1_KeyPress deben estar definidos con los parámetros adecuados a esos eventos.

La forma más fácil de crearlos es usando la "ayuda" del propio editor de Visual C# 2005, y es que al escribir el código del bucle, te permite crearlos automáticamente (pulsando la tecla TAB).
Aunque también puedes hacerlo eligiendo los eventos que queremos interceptar de cualquiera de los controles que tienes en el formulario, creando el método correspondiente y después desasociando esos eventos para que no estén repetidos. Todo eso lo puedes hacer desde el propio diseñador de formularios del IDE de Visual C# 2005 (o Visual Studio 2005).

En el código del formulario, el método asignarEventos se encarga de asignar los eventos.

 

Y esto es todo... más abajo te dejo el código fuente completo tanto para C# como para Visual Basic así como el código de la clase y el formulario de prueba (aunque sin la definición de los controles) pero para Visual C# 2005, si quieres ver el código de Visual Basic 2005 tendrás que ir al artículo para ese lenguaje.

 

El aspecto del formulario en modo de diseño es el de la siguiente figura:

El formulario en modo de diseño
El formulario en modo de diseño

 

 

Espero que te sea de utilidad.

Nos vemos.
Guillermo

Abajo del todo tienes el ZIP con el código fuente tanto para C# como para Visual Basic.

 


Código para C Sharp (C#)El código para C# (2.0 o superior)

 

El código de la clase ArrayControles

//-----------------------------------------------------------------------------
// ArrayControles                                                   (09/Abr/07)
//
// Clase-colección para simular un array de controles de Visual Basic 6.0
// Se utilizan clases generic para contener los controles.
//
// Esta clase está basada en ControlArray (09/Ago/2002),
// publicada en mi sitio el 14/Nov/2002:
// http://www.elguille.info/NET/dotnet/arrayControles.htm
//
// ©Guillermo 'guille' Som, 2002, 2007
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
 
using System.Windows.Forms;
using System.Drawing;

namespace elGuille.info.Utilidades
{

/// <summary>
/// Clase para contener controles del tipo indicado,
/// que debe derivarse de Control.
/// </summary>
/// <typeparam name="T">
/// El tipo de control que contendrá la colección.
/// </typeparam>
/// <remarks>
/// Se deriva de List&ltT&gt;
/// Autor: Guillermo 'guille' Som
/// Fecha: 09/Abr/2007
/// </remarks>
public class ArrayControles<T> : List<T>
    where T : Control
    {
 
    private string m_Nombre;
 
    /// <summary>
    /// En el constructor se debe indicar el nombre del control.
    /// </summary>
    /// <param name="elNombre">
    /// El nombre base de los controles del array,
    /// esos controles deben tener el nombre: elNombre_numero.
    /// No se admite una cadena vacía.
    /// </param>
    /// <remarks></remarks>
    public ArrayControles(string elNombre) 
        : base() 
    {
        if( String.IsNullOrEmpty(elNombre) )
        {
            throw new ArgumentException( 
                "El nombre del control no puede ser una cadena vacía");
        }
        // Asignarlo a la propiedad
        // para que se convierta en minúsculas
        // o cualquier otra comprobación.
        this.Nombre = elNombre;
    }  
 
    /// <summary>
    /// Constructor para inicializar directamente la colección de controles
    /// </summary>
    /// <param name="ctrls">
    /// Colección de controles en la que están los que debemos usar.
    /// </param>
    /// <param name="elNombre">
    /// El nombre base de los controles a tener en cuenta.
    /// </param>
    /// <remarks></remarks>
    public ArrayControles(
        string elNombre, 
        Control.ControlCollection ctrls) 
        : this(elNombre) 
    {
        //
        this.Clear();
        asignarLosControles(ctrls);
        this.Reorganizar();
    }  
 
    /// <summary>
    /// Constructor para inicializar directamente la colección de controles
    /// </summary>
    /// <param name="contenedor">
    /// El contenedor que tiene los controles a comprobar.
    /// </param>
    /// <param name="elNombre">
    /// El nombre base de los controles a tener en cuenta.
    /// </param>
    /// <remarks></remarks>
    public ArrayControles(
        string elNombre, 
        ContainerControl contenedor) 
        : this(elNombre, contenedor.Controls) 
    {
    }  

    /// <summary>
    /// Asignar los controles de la colección indicada.
    /// </summary>
    /// <param name="ctrls">
    /// Colección de controles en la que están los que debemos usar.
    /// El nombre usado será el indicado al crear la colección.
    /// </param>
    /// <remarks>
    /// La colección de controles puede ser Me.Controls
    /// ya que aquí solo se tendrán en cuenta los controles
    /// que tengan el nombre usado en esta clase,
    /// y se recorren todas las colecciones de controles que haya.
    /// </remarks>
    public void AsignarControles(Control.ControlCollection ctrls) 
    {
        this.Clear();
        asignarLosControles(ctrls);
        this.Reorganizar();
    }  
 
    /// <summary>
    /// Asignar los controles del contenedor indicado.
    /// </summary>
    /// <param name="contenedor">
    /// El contenedor de los controles en los que se buscarán
    /// los del tipo indicado en esta colección.
    /// </param>
    /// <remarks></remarks>
    public void AsignarControles(ContainerControl contenedor) 
    {
        this.Clear();
        asignarLosControles(contenedor.Controls);
        this.Reorganizar();
    }  
 
    private void asignarLosControles(Control.ControlCollection ctrls) 
    {
        // El tipo debe ser Control, para tener en cuenta todos los controles
        // que haya en la colección indicada.
        foreach(Control ctr in ctrls)
        {
            // Hacer una llamada recursiva por si este control "contiene" otros
            if( ctr.Controls.Count > 0 )
            {
                asignarLosControles(ctr.Controls);
            }

            // No tener en cuenta las mayúsculas o minúsculas
            if( ctr.Name.ToLower().IndexOf(m_Nombre) > -1 )
            {
                this.Add( ctr as T );
            }
        }
    }  

    // Sobrecargas de la propiedad predeterminada
 
    /// <summary>
    /// Propiedad predeterminada para devolver
    /// el control con el nombre indicado.
    /// </summary>
    /// <param name="name">
    /// El nombre del control a buscar.
    /// </param>
    /// <value></value>
    /// <returns>
    /// El control que tiene el nombre indicado.
    /// </returns>
    /// <remarks></remarks>
     public T this[string name] 
     {
        get
        {
            int index = this.Index(name);
            // Si existe, devolverlo, sino, devolver un valor nulo
            if( index == -1 )
            {
                return null;
            }
 
            return this[index];
        }
    }

    /// <summary>
    /// Sobrecarga de la propiedad predeterminada
    /// para permitir el acceso con un valor de tipo Object.
    /// Aunque el tipo debe ser del que contiene la colección,
    /// si no es así, se devuelve un valor nulo.
    /// </summary>
    /// <param name="obj">
    /// El control a comprobar
    /// </param>
    /// <value></value>
    /// <returns>
    /// Si el parámetro es del tipo adecuado,
    /// se devuelve con el tipo de la colección,
    /// si no lo es, se devuelve un valor nulo.
    /// </returns>
    /// <remarks></remarks>
    public T this[object obj]
    {
        get
        {
            // En C# cuando el parámetro es int
            // la sobrecarga de object tiene preferencia.
            if (obj.GetType() == typeof(int))
            {
                return base[(int)obj];
            }

            T ctrl = obj as T;

            return ctrl;
        }
    }
 
    /// <summary>
    /// Devuelve el índice del control de esta colección
    /// que tenga el mismo índice que el del parámetro.
    /// Ese parámetro puede ser cualquier control,
    /// y lo que se tendrá en cuenta será el nombre usado,
    /// el cual debe tener la forma nombre_indice,
    /// de forma que se devolverá el control que tenga ese mismo índice.
    /// </summary>
    /// <param name="obj">
    /// El control a comprobar si existe un índice como el indicado.
    /// Al ser de tipo Object no es necesario que sea del mismo tipo
    /// que los que contiene esta colección.
    /// </param>
    /// <returns>
    /// El índice correspondiente.
    /// Aunque no se comprueba si existe en la colección.
    /// En el caso de que el parámetro no tenga el formato adecuado,
    /// se devuelve -1.
    /// </returns>
    /// <remarks>
    /// Esta sobrecarga se puede usar para buscar el control correspondiente
    /// con el del índice de otro control, por ejemplo:
    /// i = m_TextBox1.Index(sender)
    /// Por supuesto, el parámetro debe ser de tipo Control.
    /// Este método podría estar compartido, pero debido a que su uso
    /// sería: ArrayControles&lt;TIPO&gt;.Index(sender)
    /// he preferido dejarlo como de instancia.
    /// </remarks>
    public int Index(object obj) 
    {
        Control ctrl = obj as Control;
        if( ctrl == null )
        {
            return -1;
        }
        //
        int i = -1;
        i = ctrl.Name.LastIndexOf("_");
        if( i > -1 )
        {
            i = Convert.ToInt32(ctrl.Name.Substring(i + 1));
        }
 
        return i;
    }  
 
    /// <summary>
    /// Devuelve el índice del control con el nombre indicado.
    /// </summary>
    /// <param name="name">
    /// Nombre del control a buscar en la colección.
    /// </param>
    /// <returns>
    /// Un valor de tipo entero con el índice del control.
    /// </returns>
    /// <remarks></remarks>
    public int Index(string name) 
    {
        int hallado = -1;

        for(int i= 0; i< this.Count; i++)
        {
            T ctrl = this[i];
            if( String.Compare(ctrl.Name, name, true) == 0 )
            {
                hallado = i;
                break;
            }
        }
        return hallado;
    }  
 
    /// <summary>
    /// Devuelve el índice del control indicado.
    /// </summary>
    /// <param name="ctrl">
    /// El control del que queremos averiguar el índice.
    /// </param>
    /// <returns>
    /// Un valor de tipo entero con el índice del control.
    /// </returns>
    /// <remarks></remarks>
    public int Index(T ctrl) 
    {
        int i = ctrl.Name.LastIndexOf("_");
        //
        // Si el nombre no tiene el signo _
        if( i == -1 )
        {
            i = this.IndexOf(ctrl);
        }
        else
        {
            i = Convert.ToInt32(ctrl.Name.Substring(i + 1));
        }
 
        return i;
    }  
    //
    /// <summary>
    /// La propiedad Nombre, externamente será de solo lectura.
    /// </summary>
    /// <value>El nombre de la colección de controles</value>
    /// <returns>
    /// El nombre de la colección de controles.
    /// </returns>
    /// <remarks>
    /// </remarks>
    public string Nombre
    {
        get
        {
            return m_Nombre;
        }
        private set
        {
            m_Nombre = value.ToLower();
        }
    }  
    //
    /// <summary>
    /// Reorganizar el contenido de la colección,
    /// ordenando por el índice indicado después del guión bajo
    /// </summary>
    /// <remarks></remarks>
    public void Reorganizar() 
    {
        List<T> ca = new List<T>();
        //
        for(int i= 0; i< this.Count; i++)
        {
            foreach(T ctr in this)
            {
                if( i == this.Index(ctr) )
                {
                    ca.Add(ctr);
                    break;
                }
            }
        }
        //
        this.Clear();
        foreach(T ctr in ca)
        {
            this.Add(ctr);
        }
    }  
}

}
 

El código del formulario:

//-----------------------------------------------------------------------------
// Prueba para usar la clase ArrayControles                         (09/Abr/07)
//
// Utilizando una clase generic para los controles.
//
// Basado en el ejemplo publicado en mi sitio:
// http://www.elguille.info/NET/dotnet/arrayControles.htm
//
// ©Guillermo 'guille' Som, 2002, 2007
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using elGuille.info.Utilidades;

namespace arrayControlesGeneric_cs
{

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    // Colecciones (como arrays) para contener los controles

    private ArrayControles<Label> m_Label1;
    private ArrayControles<TextBox> m_TextBox1;
    private ArrayControles<RadioButton> m_RadioButton1;
    //
    // Asignar los eventos a los controles
    private void asignarEventos()
    {
        //
        // Aquí estarán los procedimientos a asignar a cada array de controles
        //
        foreach (RadioButton opt in m_RadioButton1)
        {
            // En C# los delegados los podemos crear
            // de forma "precisa" (indicando el delegado asociado)
            // o bien de forma "relajada" (indicando solo el método).
            opt.KeyPress += RadioButton1_KeyPress;
            //opt.KeyPress += new KeyPressEventHandler(RadioButton1_KeyPress);
            // Para el evento general de recibir foco
            opt.Enter += control_Enter;
            // opt.Enter += new EventHandler(control_Enter);
        }

        foreach (TextBox txt in m_TextBox1)
        {
            txt.Enter += TextBox1_Enter;
            txt.KeyPress += TextBox1_KeyPress;
            txt.Leave += TextBox1_Leave;
            // Para el evento general de recibir foco
            txt.Enter += control_Enter;
        }

        foreach (Label lbl in m_Label1)
        {
            // Para el evento general de recibir foco
            lbl.Enter += control_Enter;
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // Asignar los controles y reorganizar los índices
        // No importa si el nombre no coincide exactamente,
        // ya que no se diferencian las mayúsculas de las minúsculas.

        m_Label1 = new ArrayControles<Label>("Label1", this);
        m_TextBox1 = new ArrayControles<TextBox>("textBox1", this);
        m_RadioButton1 = new ArrayControles<RadioButton>("RadioButton1", this);

        // Asignar sólo los eventos
        asignarEventos();

        // Asignar unos textos a las cajas de texto
        for (int i = 0; i < m_TextBox1.Count; i++)
        {
            m_TextBox1[i].Text = "Caja de textos " + i;
        }

    }

    private void control_Enter(object sender, EventArgs e)
    {
        Control ctrl = sender as Control;

        this.labelInfo.Text = "El control con el foco es: " + ctrl.Name;
    }

    private void TextBox1_Enter(object sender, EventArgs e)
    {
        // También se puede evaluar el índice
        // y usar los que correspondan con ese valor.
        //
        // Fíjate que en el Label, los valores son _0, _1 y _2
        // y en el TextBox son _00, _01 y _02,
        // aunque parece que "no coinciden",
        // lo que importa es que el valor numérico sea el adecuado.

        int index = m_TextBox1.Index(sender);
        m_TextBox1[index].SelectAll();
        // resaltar la etiqueta
        m_Label1[index].BackColor = Color.FromKnownColor(KnownColor.ControlDark);
    }

    private void TextBox1_Leave(object sender, EventArgs e)
    {
        // En este caso, esto es más cómodo:
        // ya que solo queremos el índice.
        int index = m_TextBox1.Index(sender);

        // poner la etiqueta con el color normal
        m_Label1[index].BackColor = Color.FromKnownColor(KnownColor.Control);
    }

    private void TextBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (int)Keys.Return)
        {
            int index = m_TextBox1.Index(sender);

            if (index == m_TextBox1.Count - 1)
            {
                m_RadioButton1[0].Focus();
            }
            else
            {
                m_TextBox1[index + 1].Focus();
            }

            // Evitar el pitido
            e.Handled = true;
        }
    }

    private void RadioButton1_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (int)Keys.Return)
        {
            int index = m_RadioButton1.Index(sender);
            //
            if (index == 0)
            {
                m_RadioButton1[index + 1].Focus();
            }
            else
            {
                m_TextBox1[0].Focus();
            }
        }
    }

    private void btnSalir_Click(object sender, EventArgs e)
    {
        this.Close();
    }
}

}

 


Espacios de nombres usados en el código de este artículo:

System.Windows.Forms
System.Drawing
System.Collections.Generic

elGuille.info.Utilidades
 


Código de ejemplo (comprimido):

 

Código de ejemplo para Visual Basic y C# 2005: array_controles_generic_2005.zip - 32.0 KB

(MD5 checksum: D279DA4548A708CC141CF67BF9822F32)

 


Ir al índice principal de el Guille

Valid XHTML 1.0 Transitional