VB.NET Lo que sucedió con las matrices de control

La omisión de las matrices de control de VB.NET es un desafío para quienes enseñan sobre matrices.

  • Ya no es posible simplemente copiar un control, como un cuadro de texto, y luego pegarlo (una o varias veces) para crear una matriz de control.
  • El código de VB.NET para crear una estructura similar a una matriz de control ha sido, en todos los libros de VB.NET que he comprado y en línea, mucho más largo y mucho más complejo. Carece de la simplicidad de codificar una matriz de control que se encuentra en VB6.

Si hace referencia a la biblioteca de compatibilidad VB6, hay objetos allí que actúan más o menos como matrices de control. Para ver a qué me refiero, simplemente use el asistente de actualización de VB.NET con un programa que contenga una matriz de control. El código es feo de nuevo, pero funciona. La mala noticia es que Microsoft no garantizará que los componentes de compatibilidad continuarán siendo compatibles, y no se supone que los use..

El código VB.NET para crear y usar "matrices de control" es mucho más largo y mucho más complejo.

Según Microsoft, para hacer algo incluso cercano a lo que puede hacer en VB 6 se requiere la creación de un "componente simple que duplique la funcionalidad de la matriz de control".

Necesita tanto una nueva clase como un formulario de alojamiento para ilustrar esto. La clase en realidad crea y destruye nuevas etiquetas. El código de clase completo es el siguiente:

Etiqueta de clase pública
    Hereda System.Collections.CollectionBase
    PrivateForm de solo lectura como _
    System.Windows.Forms.Form
    Función pública AddNewLabel () _
    Como System.Windows.Forms.Label
        'Crear una nueva instancia de la clase Label.
        Dim aLabel como nuevo sistema.Windows.Forms.Label
        'Agregar la etiqueta a la colección
    'lista interna.
        Me.List.Add (aLabel)
        'Agregar la etiqueta a la colección de controles   
        'del formulario al que hace referencia el campo HostForm.
        HostForm.Controls.Add (aLabel)
        'Establecer propiedades iniciales para el objeto Label.
        aLabel.Top = Count * 25
        aLabel.Width = 50
        aLabel.Left = 140
        aLabel.Tag = Me.Count
        aLabel.Text = "Label" & Me.Count.ToString
        Regresar aLabel
    Función final
    Public Sub New (_
    Host de ByVal como System.Windows.Forms.Form)
        HostForm = host
        Me.AddNewLabel ()
    End Sub
    Propiedad pública de solo lectura predeterminada _
        Artículo (índice ByVal como entero) como _
        System.Windows.Forms.Label
        Obtener
            Devuelve CType (Me.List.Item (Index), _
        System.Windows.Forms.Label)
        End Get
    Terminar propiedad
    Public Sub Remove ()
        'Compruebe para asegurarse de que hay una etiqueta para eliminar.
        If Me.Count> 0 Entonces
            'Eliminar la última etiqueta agregada a la matriz 
            'de la colección de controles de formulario de host. 
        'Tenga en cuenta el uso de la propiedad predeterminada en 
            'acceder a la matriz.
            HostForm.Controls.Remove (Me (Me.Count - 1))
            Me.List.RemoveAt (Me.Count - 1)
        Terminara si
    End Sub
Clase final

Para ilustrar cómo se usaría este código de clase, puede crear un formulario que lo llame. Tendría que usar el código que se muestra a continuación en el formulario:

Public Class Form1 Hereda System.Windows.Forms.Form #Region "Código generado por el Diseñador de formularios de Windows" 'También debe agregar la instrucción:' MyControlArray = New LabelArray (Me) 'después de la llamada InitializeComponent () en el' código de región oculto. 'Declarar un nuevo objeto ButtonArray. Dim MyControlArray As LabelArray Private Sub btnLabelAdd_Click (_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnLabelAdd.Click 'Call the AddNewLabel method' de MyControlArray. MyControlArray.AddNewLabel () 'Cambiar la propiedad BackColor' del Botón 0. MyControlArray (0) .BackColor = _ System.Drawing.Color.Red End Sub Private Sub btnLabelRemove_Click (_ ByVal remitente como System.Object, _ ByVal e As System .EventArgs) _ Maneja btnLabelRemove. Haga clic en 'Llamar al método Remove de MyControlArray. MyControlArray.Remove () End Sub End End Class

Primero, ¡esto ni siquiera hace el trabajo en Design Time como solíamos hacerlo en VB 6! Y segundo, no están en una matriz, están en una Colección VB.NET, algo muy diferente a una matriz..

La razón por la que VB.NET no admite la "matriz de control" de VB 6 es que no existe una "matriz" de "control" (tenga en cuenta el cambio de comillas). VB 6 crea una colección detrás de escena y la hace aparecer como una matriz para el desarrollador. Pero no es una matriz y tiene poco control sobre ella más allá de las funciones proporcionadas a través del IDE.

VB.NET, por otro lado, lo llama lo que es: una colección de objetos. Y le entregan las llaves del reino al desarrollador creando todo al aire libre.

Como ejemplo del tipo de ventajas que esto le da al desarrollador, en VB 6 los controles tenían que ser del mismo tipo y tenían que tener el mismo nombre. Dado que estos son solo objetos en VB.NET, puede convertirlos en diferentes tipos y darles diferentes nombres y aún así administrarlos en la misma colección de objetos.

En este ejemplo, el mismo evento Click maneja dos botones y una casilla de verificación y muestra en cuál se hizo clic. Haga eso en una línea de código con VB 6!

Private Sub MixedControls_Click (_
    Remitente ByVal como System.Object, _
    ByVal e As System.EventArgs) _
    Botón de manijas 1.Haga clic en _
            Button2.Click, _
            CheckBox1.Click
    'La siguiente declaración tiene que ser una declaración larga!
    'Está en cuatro líneas aquí para mantenerlo angosto
    'suficiente para caber en una página web
    Label2.Text = 
    Microsoft.VisualBasic.Right (remitente.GetType.ToString, 
    Len (remitente. GetType.ToString) - 
    (InStr (remitente. GetType.ToString, "Formularios") + 5))
End Sub

El cálculo de la subcadena es un poco complejo, pero no es realmente de lo que estamos hablando aquí. Podrías hacer cualquier cosa en el evento Click. Podría, por ejemplo, usar el Tipo de control en una instrucción If para hacer diferentes cosas para diferentes controles.

Comentarios de Frank's Computing Studies Group sobre matrices

Frank's Study Group proporcionó un ejemplo con un formulario que tiene 4 etiquetas y 2 botones. El botón 1 borra las etiquetas y el botón 2 las llena. Es una buena idea volver a leer la pregunta original de Frank y observar que el ejemplo que usó fue un bucle que se usa para borrar la propiedad Caption de una matriz de componentes Label. Aquí está el equivalente VB.NET de ese código VB 6. Este código hace lo que Frank pidió originalmente!

La clase pública Form1 hereda System.Windows.Forms.Form #Region "Código generado por el Diseñador de formularios de Windows" Dim LabelArray (4) As Label 'declara una matriz de etiquetas Private Sub Form1_Load (_ ByVal remitente como System.Object, _ ByVal e As System .EventArgs) _ Maneja MyBase.Load SetControlArray () End Sub Sub SetControlArray () LabelArray (1) = Label1 LabelArray (2) = Label2 LabelArray (3) = Label3 LabelArray (4) = Label4 End Sub Private Sub Button1_Click (_ ByVal remitente Como System.Object, _ ByVal e As System.EventArgs) _ Handles Button1.Click 'Button 1 Clear Array Dim a As Integer For a = 1 To 4 LabelArray (a) .Text = "" Next End Sub Private Sub Button2_Click (_ ByVal remitente como System.Object, _ ByVal e As System.EventArgs) _ Handles Button2.Click 'Button 2 Fill Array Dim a As Integer For a = 1 To 4 LabelArray (a) .Text = _ "Control Array" & CStr ( a) Siguiente Fin Sub End Clase

Si experimenta con este código, descubrirá que además de establecer las propiedades de las Etiquetas, también puede llamar a los métodos. Entonces, ¿por qué yo (y Microsoft) nos tomamos tantas molestias para crear el código "Feo" en la Parte I del artículo??

Tengo que estar en desacuerdo con que es realmente una "matriz de control" en el sentido clásico de VB. La matriz de control VB 6 es una parte compatible de la sintaxis de VB 6, no solo una técnica. De hecho, quizás la forma de describir este ejemplo es que es una matriz de controles, no una matriz de controles.

En la Parte I, me quejé de que el ejemplo de Microsoft SOLO funcionaba en tiempo de ejecución y no en tiempo de diseño. Puede agregar y eliminar controles de un formulario dinámicamente, pero todo tiene que implementarse en código. No puede arrastrar y soltar controles para crearlos como puede hacerlo en VB 6. Este ejemplo funciona principalmente en tiempo de diseño y no en tiempo de ejecución. No puede agregar y eliminar controles dinámicamente en tiempo de ejecución. En cierto modo, es todo lo contrario del ejemplo de la Parte I.

El clásico ejemplo de matriz de control VB 6 es el mismo que se implementa en el código VB .NET. Aquí en el código VB 6 (esto está tomado de Mezick & Hillier, Guía de examen de certificación de Visual Basic 6, p 206 - ligeramente modificado, ya que el ejemplo en el libro da como resultado controles que no se pueden ver):

Dim MyTextBox como VB.TextBox Static intNumber como Integer intNumber = intNumber + 1 Establezca MyTextBox = _ Me.Controls.Add ("VB.TextBox", _ "Text" & intNumber) MyTextBox.Text = MyTextBox.Name MyTextBox.Visible = MyTextBox.Left = _ (intNumber - 1) * 1200

Pero como Microsoft (y yo) estamos de acuerdo, las matrices de control VB 6 no son posibles en VB.NET. Entonces, lo mejor que puede hacer es duplicar la funcionalidad. Mi artículo duplicó la funcionalidad encontrada en el ejemplo de Mezick & Hillier. El código del Grupo de estudio duplica la funcionalidad de poder establecer propiedades y métodos de llamada.

Entonces, la conclusión es que realmente depende de lo que quieras hacer. VB.NET no tiene todo envuelto como parte del lenguaje, aún así, pero en última instancia es mucho más flexible.

La toma de control de John Fannon sobre matrices

John escribió: necesitaba matrices de control porque quería poner una tabla simple de números en un formulario en tiempo de ejecución. No quería las náuseas de colocarlos todos individualmente y quería usar VB.NET. Microsoft ofrece una solución muy detallada a un problema simple, pero es un mazo muy grande para romper una tuerca muy pequeña. Después de experimentar un poco, finalmente encontré una solución. Así es como lo hice.

El ejemplo de Acerca de Visual Basic anterior muestra cómo puede crear un cuadro de texto en un formulario creando una instancia del objeto, estableciendo propiedades y agregándolo a la colección Controls que forma parte del objeto Form.

Dim txtDataShow As New TextBox
txtDataShow.Height = 19
txtDataShow.Width = 80
txtDataShow.Location = Nuevo punto (X, Y)
Me.Controls.Add (txtDataShow)
Aunque la solución de Microsoft crea una Clase, razoné que sería posible incluir todo esto en una subrutina. Cada vez que llama a esta subrutina, crea una nueva instancia del cuadro de texto en el formulario. Aquí está el código completo:

Formulario de clase pública1
    Hereda System.Windows.Forms.Form

#Region "Código generado por el Diseñador de formularios de Windows"

    Private Sub BtnStart_Click (_
        Remitente ByVal como System.Object, _
        ByVal e As System.EventArgs) _
        Maneja btnStart.Click

        Dim I como entero
        Dim sData As String
        Para I = 1 a 5
            sData = CStr (I)
            Llame a AddDataShow (sData, I)
        próximo
    End Sub
    Sub AddDataShow (_
        ByVal sText As String, _
        ByVal I como entero)

        Dim txtDataShow As New TextBox
        Dim UserLft, UserTop como entero
        Dim X, Y como entero
        UserLft = 20
        UserTop = 20
        txtDataShow.Height = 19
        txtDataShow.Width = 25
        txtDataShow.TextAlign = _
            HorizontalAlignment.Center
        txtDataShow.BorderStyle = _
            BorderStyle.FixedSingle
        txtDataShow.Text = sText
        X = UserLft
        Y = UserTop + (I - 1) * txtDataShow.Height
        txtDataShow.Location = Nuevo punto (X, Y)
        Me.Controls.Add (txtDataShow)
    End Sub
Clase final
Muy buen punto, John. Esto es ciertamente mucho más simple que el código de Microsoft ... así que me pregunto por qué insistieron en hacerlo de esa manera?

Para comenzar nuestra investigación, intentemos cambiar una de las asignaciones de propiedad en el código. Cambiemos

txtDataShow.Height = 19
a

txtDataShow.Height = 100
solo para asegurarse de que haya una diferencia notable.

Cuando volvemos a ejecutar el código, obtenemos ... ¿Qué? ... lo mismo. No hay cambio en absoluto. De hecho, puede mostrar el valor con una declaración como MsgBox (txtDataShow.Height) y aún así obtiene 20 como valor de la propiedad sin importar lo que le asigne. ¿Por qué sucede eso??

La respuesta es que no estamos derivando nuestra propia Clase para crear los objetos, solo estamos agregando cosas a otra Clase, así que tenemos que seguir las reglas de la otra clase. Y esas reglas establecen que no puede cambiar la propiedad Altura. (Bien ... puedes. Si cambias la propiedad Multilínea a Verdadero, entonces puedes cambiar la Altura).

¿Por qué VB.NET sigue adelante y ejecuta el código sin siquiera un gemido de que podría haber algo mal cuando, de hecho, ignora por completo su declaración? Sin embargo, podría sugerir al menos una advertencia en la compilación. (¡Sugerencia! ¡Sugerencia! ¡Sugerencia! ¿Microsoft está escuchando?)

El ejemplo de la Parte I hereda de otra Clase, y esto hace que las propiedades estén disponibles para el código en la Clase heredada. Cambiar la propiedad Altura a 100 en este ejemplo nos da los resultados esperados. (De nuevo ... un descargo de responsabilidad: cuando se crea una nueva instancia de un componente Label grande, cubre el anterior. Para ver realmente los nuevos componentes Label, debe agregar el método llamado aLabel.BringToFront ()).

Este simple ejemplo muestra que, aunque PODEMOS simplemente agregar objetos a otra Clase (y a veces esto es lo correcto), la programación del control sobre los objetos requiere que los derivemos en una Clase y de la manera más organizada (me atrevo a decir, "la forma .NET" ??) es crear propiedades y métodos en la nueva Clase derivada para cambiar las cosas. John no se convenció al principio. Dijo que su nuevo enfoque se adapta a su propósito a pesar de que existen limitaciones de no ser "COO" (Orientado correctamente a objetos). Más recientemente, sin embargo, John escribió,

"... después de escribir un conjunto de 5 cuadros de texto en tiempo de ejecución, quería actualizar los datos en una parte posterior del programa, pero nada cambió, los datos originales todavía estaban allí.

Descubrí que podía solucionar el problema escribiendo código para quitar los cuadros antiguos y volviéndolos a poner con datos nuevos. Una mejor manera de hacerlo sería usar Me.Refresh. Pero este problema me ha llamado la atención sobre la necesidad de proporcionar un método para restar los cuadros de texto y agregarlos ".

El código de John utilizó una variable global para realizar un seguimiento de cuántos controles se habían agregado al formulario, por lo que un método ...

Private Sub Form1_Load (_
   Remitente ByVal como System.Object, _
   ByVal e As System.EventArgs) _
   Maneja MyBase.Load
   CntlCnt0 = Me.Controls.Count
End Sub

Entonces el "último" control podría ser eliminado ...

N = Me.Controls.Count - 1
Me.Controls.RemoveAt (N)
John señaló que "tal vez esto sea un poco torpe".

Es la forma en que Microsoft realiza un seguimiento de los objetos en COM Y en su código de ejemplo "feo" anterior.

Ahora he vuelto al problema de crear dinámicamente controles en un formulario en tiempo de ejecución y he estado mirando nuevamente los artículos 'Qué pasó con las matrices de control'.

He creado las clases y ahora puedo colocar los controles en el formulario de la forma en que quiero que sean.

John demostró cómo controlar la ubicación de los controles en un cuadro de grupo usando las nuevas clases que ha comenzado a usar. Quizás Microsoft tenía razón en su solución "fea" después de todo!