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

Crear (y usar) arrays de controles en .NET

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

 

Pulsa aquí para ver el código para Visual Basic 2005 usando una colección generic
Pulsa aquí para ver el código para Visual C# 2005 usando una colección generic

 

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

Código para C Sharp (C#)

 

Introducción:

En las versiones clásicas de Visual Basic, podíamos crear arrays de controles, lo cual nos permitía acceder a esos controles de la forma habitual de los arrays: Text1(0).Text = "lo que sea".
Pero en .NET Framework, (no sólo en VB.NET), no existe esa posibilidad. Bueno, casi, ya que si conviertes el código de tu aplicación de VB6 a VB.NET, el asistente te creará un array de controles, pero...

Puede que este código que te muestro no sea el "mejor" para simular los arrays de controles, pero si le "pillas" (por no decir coges), el tranquillo, la verdad es que resulta fácil de usar y, lo más importante, de implementar.
Ya que sólo tienes que hacer un par de cosillas para que todo esto esté disponible, con la ventaja de que si añades nuevos controles, prácticamente no tienes que hacer nada... salvo lo que en principio hagas para manejar esos arrays... me explico, si has convertido el código desde VB6 ya tendrás creado el código, pero si añades nuevos controles, te tienes que "preocupar" de que esos nuevos controles formen parte del array.
Con el código que te propongo no tienes que preocuparte de nada... salvo crear las referencias a la clase que contendrá los controles e inicializarlas...
Bueno, también tendrás que añadir o crear los eventos que manejarán esos controles... siempre y cuando quieras que todo sea "automático", ya que también puedes hacerlo por las "bravas", (es decir, interceptar por separado los eventos de cada control), en cuyo caso no tiene sentido usar el código que te propongo...

Te resumo brevemente los "requisitos" para poder usar esto de los arrays de controles:
1- Tienes que añadir al proyecto el fichero que contiene la declaración de la clase ControlArray
2- Los controles que quieras que estén en el array (un array para cada tipo de control o grupo de controles relacionados) deberán tener el mismo nombre y deberán acabar con el signo de subrayado bajo seguido de un número que actuará de índice. Por tanto es recomendable para que la cosa funcione bien, de que esos valores sean consecutivos y que empiecen por cero (lee la nota).
3- Hacer una llamada al método AsignarControles (desde el Form_Load)
4- Asignar los eventos que interceptarán (esto sólo es necesario si quieres que un mismo evento sea válido para todos los controles del array)

Nota del 09/Abr/07:
Los índices a usar en los controles deben empezar por cero, no solo porque los arrays en .NET empiezan por cero y así el uso es más lógico, sino también porque esta clase la creé pensando en los que usábamos arrays de controles de Visual Basic 6.0 (o anterior) y cuando creábamos un array de controles, el primero de la lista era el que tenía el índice cero.

Por tanto, si vas a crear un array de 3 controles TextBox, cuyo nombre será TextBox1, los controles a añadir a ese array deben llamarse: TextBox1_0, TextBox1_1 y TextBox1_2. De esa forma podrás acceder con un bucle desde 0 hasta 1 menos del número de elementos del array y otras cosas que normalmente harás con los arrays.

Espero que esta nota, aunque llegue con varios años de retraso, sirva para los que leen este artículo por primera vez.

En cuanto veas el ejemplo, te darás cuenta que no es tan complicado.

Aquí te muestro las ventajas de asignar los eventos y manipularlos desde un mismo procedimiento, en este caso, cuando el control recibe el foco, selecciona todo el texto y cambia el color de la etiqueta que tiene el mismo índice:

Private Sub TextBox1_Enter(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim txt As TextBox = CType(sender, TextBox)
    Dim Index As Integer = m_TextBox1.Index(txt)
    '
    txt.SelectAll()
    ' resaltar la etiqueta
    m_Label1(Index).BackColor = Color.FromKnownColor(KnownColor.ControlDark)
End Sub

En el formulario de ejemplo, se procesan estos eventos:
TextBox.KeyPress, cuando se pulsa Intro se pasa al siguiente control del array, si es el último, se pasa el foco al primer Option.
TextBox.Enter, (cuando tiene el foco), se selecciona todo el texto y se resalta la etiqueta con el mismo índice.
TextBox.Leave, (cuando pierde el foco), la etiqueta con el mismo índice se deja con el color predeterminado.
RadioButton.KeyPress, al pulsar Intro se pasa al siguiente Option y si es el último, se pasa al primer TextBox.

 

Espero que te sea de utilidad.

Nos vemos.
Guillermo

 

Aquí tienes el código tanto para VB como para C#: ArrayControles.zip 17.6 KB


Código para Visual Basic.NET (VB.NET)Crear array de controles con Visual Basic .NET

Aquí tienes el código de la clase ControlArray, así como el código del formulario en el que implementar esto de los arrays de controles.

La clase ControlArray

'------------------------------------------------------------------------------
' ControlArray                                                      (09/Ago/02)
' Clase para simular un array de controles
'
' ©Guillermo 'guille' Som, 2002
'------------------------------------------------------------------------------
Option Strict On

'<summary>
' Colección de objetos de tipo Control
'</summary>
Public Class ControlArray
    Inherits CollectionBase
    '
    Private mNombre As String
    '
    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(ByVal elNombre As String)
        MyBase.New()
        mNombre = elNombre
    End Sub
    '
    Public Function Add(ByVal ctrl As Control) As Integer
        Return MyBase.List.Add(ctrl)
    End Function
    '
    ' Asignar los controles que contendrá esta colección            (14/Nov/02)
    Public Sub AsignarControles(ByVal ctrls As Control.ControlCollection, _
                    Optional ByVal elNombre As String = "")
        ' Asignar los controles a los arrays,
        ' para que esto funcione automáticamente los nombres de los controles
        ' deberían tener el formato: nombre_numero
        '   nombre del control seguido de un guión bajo y el índice
        If elNombre = "" Then
            elNombre = mNombre
        End If
        ' si no se indica el nombre de los controles a añadir,
        ' lanzar una excepción
        If elNombre = "" Then
            Throw New ArgumentException( _
                        "No se ha indicado el nombre base de los controles")
            Exit Sub
        End If
        '
        Me.Clear()
        asignarLosControles(ctrls, elNombre)
        Me.Reorganizar()
    End Sub
    Private Sub asignarLosControles( _
                    ByVal ctrls As Control.ControlCollection, _
                    ByVal elNombre As String)
        Dim ctr As Control
        '
        For Each ctr In ctrls
            ' Hacer una llamada recursiva por si este control "contiene" otros
            asignarLosControles(ctr.Controls, elNombre)
            '
            If ctr.Name.IndexOf(elNombre) > -1 Then
                Me.Add(ctr)
            End If
        Next
    End Sub
    '
    Public Function Contains(ByVal ctrl As Control) As Boolean
        list.Contains(ctrl)
    End Function
    '
    Public Function IndexOf(ByVal ctrl As Control) As Integer
        Return list.IndexOf(ctrl)
    End Function
    '
    Public Function Index(ByVal name As String) As Integer
        Dim ctrl As Control
        Dim i As Integer
        Dim hallado As Integer = -1
        '
        For i = 0 To List.Count - 1
            ctrl = CType(list(i), Control)
            If StrComp(ctrl.Name, name, CompareMethod.Text) = 0 Then
                hallado = i
                Exit For
            End If
        Next
        Return hallado
    End Function
    Public Function Index(ByVal ctrl As Control) As Integer
        Dim i As Integer
        '
        i = ctrl.Name.LastIndexOf("_")
        ' Si el nombre no tiene el signo _
        If i = -1 Then
            i = list.IndexOf(ctrl)
        Else
            i = CInt(ctrl.Name.Substring(i + 1))
        End If
        Return i
    End Function
    '
    Public Sub Insert(ByVal index As Integer, ByVal ctrl As Control)
        List.Insert(index, ctrl)
    End Sub
    '
    Default Public Property Item(ByVal index As Integer) As Control
        Get
            Return CType(List.Item(index), Control)
        End Get
        Set(ByVal Value As Control)
            list.Item(index) = Value
        End Set
    End Property
    Default Public Property Item(ByVal name As String) As Control
        Get
            Dim index As Integer = Me.Index(name)
            ' Si existe, devolverlo, sino, crear uno nuevo
            If index = -1 Then
                'index = Me.Add(ctrl)
            End If
            Return CType(List.Item(index), Control)
        End Get
        Set(ByVal Value As Control)
            Dim index As Integer = Me.Index(name)
            If index = -1 Then
                index = Me.Add(Value)
            End If
            list.Item(index) = Value
        End Set
    End Property
    Default Public Property Item(ByVal ctrl As Control) As Control
        Get
            Return CType(List(Me.IndexOf(ctrl)), Control)
        End Get
        Set(ByVal Value As Control)
            List(Me.IndexOf(ctrl)) = Value
        End Set
    End Property
    '
    Public Property Nombre() As String
        Get
            Return mNombre
        End Get
        Set(ByVal Value As String)
            mNombre = Value
        End Set
    End Property
    '
    Public Sub Remove(ByVal ctrl As Control)
        List.Remove(ctrl)
    End Sub
    '
    ' Reorganizar el contenido de la colección y ordenar por índice
    Public Sub Reorganizar()
        Dim ca As New ControlArray()
        Dim ctr As Control
        Dim i As Integer
        '
        For i = 0 To Me.Count - 1
            For Each ctr In Me
                If i = Me.Index(ctr) Then
                    ca.Add(ctr)
                    Exit For
                End If
            Next
        Next
        '
        Me.Clear()
        For Each ctr In ca
            Me.Add(ctr)
        Next
    End Sub
End Class

 

El formulario de ejemplo

'------------------------------------------------------------------------------
' Prueba para usar la clase ControlArray con VB .NET                (14/Nov/02)
'
' ©Guillermo 'guille' Som, 2002
'------------------------------------------------------------------------------
Option Strict On

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Código generado por el Diseñador de Windows Forms "
    '...
#End Region
    '
    ' Arrays para contener los controles
    ' (definir los arrays que vamos a usar)
    Private m_Label1 As New ControlArray("Label1")
    Private m_TextBox1 As New ControlArray("TextBox1")
    Private m_RadioButton1 As New ControlArray("RadioButton1")
    '
    ' Asignar los eventos a los controles
    Private Sub asignarEventos()
        Dim txt As TextBox
        Dim opt As RadioButton
        '
        ' Aquí estarán los procedimientos a asignar a cada array de controles
        '
        For Each opt In m_RadioButton1
            AddHandler opt.KeyPress, AddressOf RadioButton1_KeyPress
        Next
        For Each txt In m_TextBox1
            AddHandler txt.Enter, AddressOf TextBox1_Enter
            AddHandler txt.KeyPress, AddressOf TextBox1_KeyPress
            AddHandler txt.Leave, AddressOf TextBox1_Leave
        Next
        '
    End Sub
    '
    Private Sub Form1_Load(ByVal sender As Object, _
                    ByVal e As System.EventArgs) Handles MyBase.Load
        '
        ' Asignar los controles y reorganizar los índices
        m_Label1.AsignarControles(Me.Controls)
        m_TextBox1.AsignarControles(Me.Controls)
        m_RadioButton1.AsignarControles(Me.Controls)

        ' Asignar sólo los eventos
        asignarEventos()
    End Sub
    '
    Private Sub TextBox1_Enter(ByVal sender As Object, _
                    ByVal e As System.EventArgs)
        '
        Dim txt As TextBox = CType(sender, TextBox)
        Dim Index As Integer = m_TextBox1.Index(txt)
        '
        txt.SelectAll()
        ' resaltar la etiqueta
        m_Label1(Index).BackColor = Color.FromKnownColor(KnownColor.ControlDark)
        '
    End Sub
    Private Sub TextBox1_Leave(ByVal sender As Object, ByVal e As EventArgs)
        '
        Dim txt As TextBox = CType(sender, TextBox)
        Dim Index As Integer = m_TextBox1.Index(txt)
        '
        ' poner la etiqueta con el color normal
        m_Label1(Index).BackColor = Color.FromKnownColor(KnownColor.Control)
    End Sub
    Private Sub TextBox1_KeyPress(ByVal sender As Object, _
                    ByVal e As System.Windows.Forms.KeyPressEventArgs)
        '
        If e.KeyChar = ChrW(Keys.Return) Then
            Dim txt As TextBox = CType(sender, TextBox)
            Dim Index As Integer = m_TextBox1.Index(txt)
            '
            If Index = 2 Then
                m_RadioButton1(0).Focus()
            Else
                m_TextBox1(Index + 1).Focus()
            End If
        End If
    End Sub
    '
    Private Sub RadioButton1_KeyPress(ByVal sender As Object, _
                    ByVal e As System.Windows.Forms.KeyPressEventArgs)
        If e.KeyChar = ChrW(Keys.Return) Then
            Dim opt As RadioButton = CType(sender, RadioButton)
            Dim Index As Integer = m_RadioButton1.Index(opt)
            '
            If Index = 0 Then
                m_RadioButton1(Index + 1).Focus()
            Else
                m_TextBox1(0).Focus()
            End If
        End If
    End Sub
    '
    Private Sub btnCerrar_Click(ByVal sender As Object, _
                    ByVal e As System.EventArgs) _
                    Handles btnCerrar.Click
        Me.Close()
    End Sub
End Class

Nota del 09/Abr/07:
En el código original, el texto resaltado estaba mal, ya que era m_TextBox1, cuando debe ser m_RadioButton1.


Código para C Sharp (C#)Crear array de controles con C#

Aquí tienes el código de la clase ControlArray, así como el código del formulario en el que implementar esto de los arrays de controles.

La clase ControlArray

//-----------------------------------------------------------------------------
// ControlArray                                                     (14/Nov/02)
// Clase para simular un array de controles
//
// Versión para C#
//
// ©Guillermo 'guille' Som, 2002
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Windows.Forms; 

//
// Colección de objetos de tipo Control
//
namespace ArrayControlesCS
{
    public class ControlArray : CollectionBase
    {
        //
        private string mNombre;
        //
        public ControlArray()
        {
        }
        public ControlArray(string elNombre){
            mNombre = elNombre;
        }
        //
        public int Add(Control ctrl)
        {
            return base.List.Add(ctrl);
        }
        //
        // Asignar los controles que contendrá esta colección       (14/Nov/02)
        public void AsignarControles(Control.ControlCollection ctrls)
        {
            string elNombre = mNombre;
            this.AsignarControles(ctrls, elNombre); 
        }
        public void AsignarControles( 
                    Control.ControlCollection ctrls, string elNombre)
        {
            // Asignar los controles a los arrays,
            // para que esto funcione automáticamente los nombres de los controles
            // deberían tener el formato: nombre_numero
            //  nombre del control seguido de un guión bajo y el índice
            if( elNombre == null )
                elNombre = mNombre;
            // si no se indica el nombre de los controles a añadir,
            // lanzar una excepción
            if( elNombre == "" )
                throw new ArgumentException( 
                          "No se ha indicado el nombre base de los controles");
            //
            this.Clear();
            asignarLosControles(ctrls, elNombre);
            this.Reorganizar();
        }
        private void asignarLosControles( 
                    Control.ControlCollection ctrls, string elNombre)
        {
            foreach(Control ctr in ctrls){
                // Hacer una llamada recursiva por si este control "contiene" otros
                asignarLosControles(ctr.Controls, elNombre);
                //
                if( ctr.Name.IndexOf(elNombre) > -1 )
                    this.Add(ctr);
            }
        }
        //
        public bool Contains(Control ctrl)
        {
            return base.List.Contains(ctrl);
        }
        public int IndexOf(Control ctrl)
        {
            return base.List.IndexOf(ctrl);
        }
        public int Index(string name)
        {
            Control ctrl;
            int hallado = -1;
            //
            for(int i=0; i<= List.Count - 1; i++){
                ctrl = (Control)base.List[i];
                if( ctrl.Name == name ){
                    hallado = i;
                    break;
                }
            }
            return hallado;
        }
        public int Index(Control ctrl)
        {
            int i;
            //
            i = ctrl.Name.LastIndexOf("_");
            // Si el nombre no tiene el signo 
            if( i == -1 ){
                i = base.List.IndexOf(ctrl);
            }else{
                i = Convert.ToInt32(ctrl.Name.Substring(i + 1));
            }
            return i;
        }
        //
        void Insert(int index, Control ctrl)
        {
            List.Insert(index, ctrl);
        }
        //
        public Control this[int index]
        {
            get{
                return ((Control)List[index]);
            }
            set{
                base.List[index] = value;
            }
        }
        public Control this[string name] 
        {
            get{
                int index = this.Index(name);
                return ((Control)List[index]);
            }
            set{
                int index = this.Index(name);
                if( index == -1 ){
                    index = this.Add(value);
                }
                base.List[index] = value;
            }
        }
        public Control this[Control ctrl]
        {
            get{
                return (Control)List[this.IndexOf(ctrl)];
            }
            set{
                List[this.IndexOf(ctrl)] = value;
            }
        }
        //
        public string Nombre
        {
            get{
                return mNombre;
            }
            set{
                mNombre = value;
            }
        }
        //
        void Remove(Control ctrl)
        {
            List.Remove(ctrl);
        }
        //
        // Reorganizar el contenido de la colección y ordenar por índice
        public void Reorganizar()
        {
            ControlArray ca = new ControlArray();
            //
            for(int i=0; i<= this.Count - 1; i++){
                foreach(Control ctr in this){
                    if( i == this.Index(ctr) ){
                        ca.Add(ctr);
                        break;
                    }
                }
            }
            //
            this.Clear();
            foreach(Control ctr in ca){
                this.Add(ctr);
            }
        }
    }
}

El formulario de ejemplo

/*
'------------------------------------------------------------------------------
' Prueba para usar la clase ControlArray con C#                     (14/Nov/02)
'
' ©Guillermo 'guille' Som, 2002
'------------------------------------------------------------------------------
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace ArrayControlesCS
{
    /// 
    /// Descripción breve de Form1.
    /// 
    public class Form1 : System.Windows.Forms.Form
    {
        //...
        
        #region Windows Form Designer generated code
            //...
        #endregion

        //...

        // Arrays para contener los controles
        // (definir los arrays que vamos a usar)
        ControlArray m_Label1 = new  ControlArray("Label1");
        ControlArray m_TextBox1 = new  ControlArray("TextBox1");
        ControlArray m_RadioButton1 = new  ControlArray("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)
            {
                opt.KeyPress += new KeyPressEventHandler(RadioButton1_KeyPress);
            }
            foreach(TextBox txt in m_TextBox1)
            {
                txt.Enter += new System.EventHandler(TextBox1_Enter);
                txt.KeyPress += new KeyPressEventHandler(TextBox1_KeyPress);
                txt.Leave += new System.EventHandler(TextBox1_Leave);
            }
        }

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

        private void Form1_Load(object sender, System.EventArgs e)
        {
            // Asignar los controles y reorganizar los índices
            m_Label1.AsignarControles(this.Controls);
            m_TextBox1.AsignarControles(this.Controls);
            m_RadioButton1.AsignarControles(this.Controls);

            // Asignar sólo los eventos
            asignarEventos();
        }
        void TextBox1_Enter(object sender, System.EventArgs  e )
        {
            //
            TextBox txt  = ((TextBox)sender);
            int Index  = m_TextBox1.Index(txt);
            //
            txt.SelectAll();
            // resaltar la etiqueta
            m_Label1[Index].BackColor = 
                        Color.FromKnownColor(KnownColor.ControlDark);
            //
        }
        void TextBox1_Leave(object sender, System.EventArgs  e )
        {
            //
            TextBox txt  = ((TextBox)sender);
            int Index  = m_TextBox1.Index(txt);
            //
            // poner la etiqueta con el color normal
            m_Label1[Index].BackColor = 
                        Color.FromKnownColor(KnownColor.Control);
        }
        void TextBox1_KeyPress(object sender, KeyPressEventArgs  e )
        {
            //
            if( e.KeyChar == '\r' )
            {
                TextBox txt  = ((TextBox)sender);
                int Index  = m_TextBox1.Index(txt);
                //
                if( Index == 2 )
                {
                    m_RadioButton1[0].Focus();
                }
                else
                {
                    m_TextBox1[Index + 1].Focus();
                }
            }
        }
        void RadioButton1_KeyPress(object sender, KeyPressEventArgs  e )
        {
            if( e.KeyChar == '\r' )
            {
                RadioButton opt  = ((RadioButton)sender);
                int Index  = m_RadioButton1.Index(opt);
                //
                if( Index == 0 )
                {
                    m_RadioButton1[Index + 1].Focus();
                }
                else
                {
                    m_TextBox1[0].Focus();
                }
            }
        }
    }
}

Nota del 09/Abr/07:
En el código original, el texto resaltado estaba mal, ya que era m_TextBox1, cuando debe ser m_RadioButton1.


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