Matriz de controles MenuStrip partiendo modo diseño (VBasic 2013) (Solucionado)

Iniciado por Tazmania40, 6 Octubre 2015, 09:09 AM

0 Miembros y 1 Visitante están viendo este tema.

Tazmania40

Buenos días, veo que en VB6 teníamos la propiedad Index y cuando creabamos una matriz de controles te preguntaba si ponías el mismo nombre VB "Desea crear una matriz de controles...?", asi cuando empleabas el mismo evento Click solo hacía referencia al pulsado por la propiedad Index.

Eso es lo que preciso en mi programa, tengo un menú bastante extenso y ya cambiado muchas cosas y solo tengo 2 submenus que hacen referencia según lo que pulsas a un mismo código, repito por tanto un total de 20, este código.

Código (vbnet) [Seleccionar]

Private Sub MnuRepasoSuma1_Click(sender As Object, e As EventArgs) Handles MnuRepasoSuma1.Click
       Form2.Close()
       TAB = "SUMAR" : RE = 1
       Form2.Show()
End Sub

Private Sub MnuRepasoSuma2_Click(sender As Object, e As EventArgs) Handles MnuRepasoSuma2.Click
       Form2.Close()
       TAB = "SUMAR" : RE = 2
       Form2.Show()
End Sub

Private Sub MnuRepasoSuma3_Click(sender As Object, e As EventArgs) Handles MnuRepasoSuma3.Click
       Form2.Close()
       TAB = "SUMAR" : RE = 3
       Form2.Show()
End Sub
...
...


Y así 20 veces, requiero por tanto algo parecido a la matriz, pero claro no voy a crear todo el MenuStrip por código porque es muy extenso, con muchas propiedades, colores, checked, font... y tardo mucho más y quería saber si se puede agrupar un mismo evento de pulsaciones de Click, como antes se hacía con Index para aprovechar un mismo codigo y que sólo cambio una variable que es RE que según lo pulsado antes se podía jugar con esa variable Index en VB6.

Requiero hacer lo mismo pero en VB2013, haber si hay alguna forma sin la de crear todo el menú con los submenus porque sino lo dejo asi (copiar y pegar) aunque no lo veo bien. Por eso me imagino que debe existir alguna forma de agrupar eventos y que se sepa cuando pulsas el 1, el 2, el 3 y que todo vaya a un mismo evento Click.

Muchas gracias y como siempre seguiré mirando a ver si veo alguna solución.


Lekim

Hola
Básicamente una manera es esta y lo puedes poner en un evento o función:

Código (vbnet) [Seleccionar]

Public Class Form1
Dim LabelArray(6) As Label

Private sub Evento()
       For Index As Integer = 0 To LabelArray.Length - 1
           LabelArray(Index) = New Label
           Me.Controls.Add(LabelArray(Index))
       Next

End Sub
End Class





Ahora querrás que estén en un lugar u otro. Entonces debes modificar las propiedades. Además seguramente necesites usar eventos como Click o MouseEnter.

Aquí te dejo un ejemplo:
Código (vbnet) [Seleccionar]
Option Explicit On
Option Strict On
Public Class Form1
   Dim LabelArray(6) As Label
   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
       Dim StartLabel As Point = New Point(10, 10)


         For Index As Integer = 0 To LabelArray.Length - 1
           LabelArray(Index) = New Label()
           With LabelArray(Index)
               .Text = "Label" & Index
               .ForeColor = Color.Red
               .Font = New Font("Arial", 10)
               .BackColor = Color.Blue
               .TextAlign = ContentAlignment.MiddleCenter
               .Location = New Point(StartLabel.X, StartLabel.Y)
           End With
           Me.Controls.Add(LabelArray(Index)) 'Añade el control al Form
           StartLabel.Y += 30 'Incrementa la posición Y, para que los labels queden uno debajo del otro
       Next

        'Vincula   LabelArray_Click con el evento Click del control LabelArray
       For I As Integer = 0 To LabelArray.Length - 1
           AddHandler LabelArray(I).Click, AddressOf LabelArray_Click
       Next
   End Sub

   Private Sub LabelArray_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
       'Obtiene el índice del control seleccionado
       Dim Index As Integer = Array.IndexOf(LabelArray, sender)

       If Index.Equals(0) Then LabelArray(0).Text = "HOLA"
   End Sub
End Class



Añado que esto es en WindowsForms, en WPF, es algo diferente. Ahora, lo paso a  WPF





Aquí está lo mismo pero para WPF.

Código (vbnet) [Seleccionar]
Option Explicit On
Option Strict On
Class MainWindow
   Dim LabelArray(6) As Label
   Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
       Dim StartLabel As New Point(X:=50, Y:=50)
       For Index As Integer = 0 To LabelArray.Length - 1
           LabelArray(Index) = New Label()
           With LabelArray(Index)
               .Content = "Label" & Index  'Texto que muestra el Label
               .Width = 100
               .Height = 20
               .Foreground = Brushes.Red 'Color del texto
               .Background = Brushes.Blue 'Color del fondo
               .FontFamily = New FontFamily("Arial")
               .FontSize = 15
               .Padding = New Thickness(25, 1, 0, 0) 'Colocación del texto dentro del label
               .VerticalAlignment = Windows.VerticalAlignment.Top 'Colocación del label
               .HorizontalAlignment = Windows.HorizontalAlignment.Left  'Colocación del label
               .Margin = New Thickness(StartLabel.X, StartLabel.Y, 0, 0) 'Margen Izquierda, Arriba,  Derecha, Abajo
           End With
           Grid1.Children.Add(LabelArray(Index)) 'Añade el control a Grid1 (NOTA: El Grid debe ser nombrado para poder establecer esta propiedad)
           StartLabel.Y += 30
       Next

       For I As Integer = 0 To LabelArray.Length - 1
           AddHandler LabelArray(I).MouseLeftButtonDown, AddressOf LabelArray_MouseLeftButtonDown  'Vincula LabelArray con el evento LabelArray_Click y evento Click
       Next
   End Sub

   Private Sub LabelArray_MouseLeftButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
       Dim Index As Integer = Array.IndexOf(LabelArray, sender)
       If Index.Equals(0) Then LabelArray(0).Content = CStr("Hola")
   End Sub
End Class


Por cierto, por si no lo sabes hay un foro para VB.NET, como supongo que sabrás esto es para Visual Basic X

http://foro.elhacker.net/net-b62.0/

Lekim

Mis disculpas porque realmente no leí detenidamente tu pregunta  :-\

Vale, te refieres al MenuStrip. Y mira que está bien claro.

Bueno, parece ser que no se puede hacer tal y como se hace  por ejemplo con un Label, como he mostrado en el ejemplo.

Puedes hacer esto:


Código (vbnet) [Seleccionar]
Public Class Form1
   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

       Dim idMenu = "1001"
       Dim Menu1 As New ToolStripMenuItem("Menu Item")

       Menu1.DropDownItems.Add("Item_1")
       Menu1.DropDownItems.Add("Item_2")

       MenuStrip1.Items.Add(Menu1) 'Agrega el menú

       'crea eventos para Menu1
       AddHandler Menu1.DropDownItemClicked, AddressOf Menu1_click

   End Sub


   Private Sub Menu1_click(ByVal sender As Object, ByVal e As ToolStripItemClickedEventArgs)

       If e.ClickedItem.Text = "Item_1" Then Me.Text = "Item_1"
       If e.ClickedItem.Text = "Item_2" Then Me.Text = "Item_2"

   End Sub
End Class



Si prefieres trabajar con índices, se me ocurre hacer lo siguiente para el evento Click:

Código (vbnet) [Seleccionar]
   Private Sub Menu1_click(ByVal sender As Object, ByVal e As ToolStripItemClickedEventArgs)
       Dim IndexItem As Integer
       Select Case e.ClickedItem.Text
           Case "Item_1" : IndexItem = 0
           Case "Item_2" : IndexItem = 1
       End Select


       If IndexItem = 0 Then Me.Text = "Item1"
       If IndexItem = 1 Then Me.Text = "Item2"
   End Sub

Tazmania40

Gracias Lekim, lo primero desconocía que hubiera un subforo para .net en Programación general, a partir de ahora expongo mis preguntas ahí.

De momento solo programo con Windows Form, lo adapté todo para trabajar con Windows Form, incluso la apariencia porque ya de por sí es difícil pasar de VB6 a Visual Basic 2013, como para ponerse a programar con WPF, más bonito con web, gráficos... pero prefiero lo que ya sabía y adaptarlo, además en los programas utilizo la versión .Net Framework 3.5 para que tenga compatibilidad con XP porque tengo compañeros que utilizan todavía ese SO.

Estoy probando tu código del Label, cambiándolo por un botón y desconocía como agregar eventos a una matriz de controles.

Código (vbnet) [Seleccionar]
AddHandler LabelArray(I).Click, AddressOf LabelArray_Click

y el cómo llamar a ese evento Click o naturalmente el que pongas añadiendo  a AddHandler y comparándolo con una variable como antes se hacía con Index.

Código (vbnet) [Seleccionar]

Private Sub LabelArray_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
   'Obtiene el índice del control seleccionado
   Dim Index As Integer = Array.IndexOf(LabelArray, sender)
    If Index.Equals(0) Then LabelArray(0).Text = "HOLA"
End Sub


A todo esto se me ocurre añadir una clase o módulo, de forma que las matrices de controles no tengan definido el número de array "Dim LabelArray() As Label" y luego dimensionarlas desde el programa principal "LabelArray = New Label (NumTotal  - 1) {}" donde pongamos en la variable "NumTotal" el número de elementos que vayamos a utilizar. Veo que hay que ir haciendo procedimientos para ahorrarnos código que antes con dos pulsaciones podíamos hacer.

Voy a probar para mi programa añadiendo los elementos ToolStripMenuItem por código, seguramente genere un procedimento dentro del Formulario principal y más adelante ya crearé un módulo para ir acoplando distintos Controles de arrays y así intentar en mis programas que la utilización sea parecida como antes con VB6 e index.

MUCHAS GRACIAS, si me surge algún problema contestaré por aqui; pero lo dicho las nuevas preguntas lo haré por el foro NET.

Tazmania40

Pongo el código que ya he probado y funciona el evento Click, he cambiado algunas cosas puesto que primero el control MenuStrip lo creo en tiempo de diseño, tengo muchos menús ya creados y submenús y ponerlo todo en código es más lioso.

Solo pongo en código los 3 submenus que tienen varias opciones y que desembocan todos en un mismo click donde solo cambio una variable RE.

Código (vbnet) [Seleccionar]

Private Prueba(2) As ToolStripMenuItem             'Declaro Array de elementos

'Pongo los elementos en el submenú correspondiente y los agrego, además del evento Click
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
     Prueba(0) = New ToolStripMenuItem("Numero1")
     Prueba(1) = New ToolStripMenuItem("Numero2")
     Prueba(2) = New ToolStripMenuItem("Numero3")
     For I = 0 To 2
          MnuRepasoSuma.DropDownItems.Add(Prueba(I))
          AddHandler Prueba(I).Click, AddressOf EventoMenu
     Next I
End Sub

Private Sub EventoMenu(ByVal sender As System.Object, ByVal e As System.EventArgs)
     Dim Index As Integer = Array.IndexOf(Prueba, sender) 'Obtiene índice control seleccionado
     Form2.Close()
     TAB = "SUMAR" : RE = Index + 1
     Form2.Show()
End Sub


Naturalmente tendre que utilizar for..next para añadir propiedades restantes, como dije seguramente genere una Clase con el procedimiento de declarar array y añadir propiedades y controles.

Una última pregunta, aunque funciona bien hay que utilizar el método Dispose, he leido algo que es como el Unload Me, pero no se muy bien como codificarlo y si al emplear algún Procedimiento hay que liberar objeto y en el código que tengo donde habría que colocarlo o bien al ser un código simple no hace falta y ya se libera todo cuando se cierra el programa?

Gracias y salu2

Lekim

Hola Tazmania40

Está genial tu código. Muy buen aporte. Había visto ejemplos por ahí que usaban DropDownItems para crear los items con índices, pero los códigos estaba incompletos y no me funcionaban.

Igualmente en tu código  no has puesto la variable  MnuRepasoSuma de clase ToolStripMenuItem al que hace referencia aquí:

Código (vbnet) [Seleccionar]

MnuRepasoSuma.DropDownItems.Add(Prueba(I))



Además, ¿Qué ocurre si quiero añadir un separador. Esas líneas que hay en los menús para separar conceptos. ¿Cómo hago para crear submenus en los Items? También he despejado esa duda.


Experimentando un poco he conseguido hacerlo. He hecho este código a partir del que has facilitado para despejar esas dudas y que cualquiera puede hacer un copia y pega y probarlo, usarlo o mejorarlo.

Código (vbnet) [Seleccionar]

Option Strict On
Option Explicit On
Public Class Form1

   Private mnuStrip1 As New MenuStrip()
   Private mainToolStripMenuItem As New ToolStripMenuItem()
   Private Item(3) As ToolStripMenuItem
   Private SubItem1(3) As ToolStripMenuItem
   Private Separator(0) As ToolStripSeparator
   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

       '*****Crea la cabezera del menú  mnuStrip1 ********
       Me.mainToolStripMenuItem.Text = "Archivo"
       mnuStrip1.Items.AddRange(New ToolStripItem() {mainToolStripMenuItem}) 'Añade un submenú para añadir Items
       Me.Controls.Add(mnuStrip1)
       '**************************************************
       '
       For numItms As Integer = 0 To Item.Count - 1
           Dim name As String = Nothing
           Select Case numItms
               Case 0 : name = "Abrir"
               Case 1 : name = "Guardar"
               Case 2 : name = "Recientes"
               Case 3 : name = "Cerrar"
                   'Crea un Separador encima del Item3
                   Separator(0) = New ToolStripSeparator
                   mainToolStripMenuItem.DropDownItems.Add(Separator(0)) 'Separador
           End Select
           Item(numItms) = New ToolStripMenuItem(name)
           mainToolStripMenuItem.DropDownItems.Add(Item(numItms))

           'Añade subItems al Item 2
           If numItms = 2 Then 'Añade subItems al Item 2 (Recientes)
               For numSubItm As Integer = 0 To SubItem1.Count - 1
                   Select Case numSubItm
                       Case 0 : name = "File1"
                       Case 1 : name = "File2"
                       Case 2 : name = "File3"
                       Case 3 : name = "File4"
                   End Select
                   SubItem1(numSubItm) = New ToolStripMenuItem(name) 'Crea SubItem1 para Item2
                   Item(2).DropDownItems.Add(SubItem1(numSubItm)) 'Añade 'SubItem1
               Next
           End If
       Next

       'Crea evento click para los Items
       For I As Integer = 0 To Item.Count - 1
           AddHandler Item(I).Click, AddressOf Item_Click
       Next I
       'Crea evento click para los Subitems
       For I As Integer = 0 To SubItem1.Count - 1
           AddHandler SubItem1(I).Click, AddressOf SubItem1_Click
       Next I
   End Sub
   Private Sub Item_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
       'Obtiene el índice del Item seleccionado
       Dim Index As Integer = Array.IndexOf(Item, sender)

       'Muestra un mensaje con el texto del Item
       MessageBox.Show(Item(Index).Text)

       'Cierra la apliación si se hace click en el Item 3 de la colección
       If Index = 3 Then End
   End Sub
   Private Sub SubItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
       'Obtiene el índice del Item seleccionado
       Dim Index As Integer = Array.IndexOf(SubItem1, sender)

       'Muestra un mensaje con el texto del SubItem
       MessageBox.Show(SubItem1(Index).Text)
   End Sub
End Class


Como ves el menú 'Archivo' se crea en tiempo de ejecución. No hace falta añadir controles al formulario.

Mediante  "mnuStrip1.Items.AddRange(New ToolStripItem() {mainToolStripMenuItem})" se crea el menú para los Items y finalmente creamos el menú completo  (sin subitems) mediante  "Controls.Add(mnuStrip1)".

Depués hago lo que has hecho tú y añado el "submenu" con sus Items y su índice correspondiente, pero esta vez añadiendo un separador. La posición de cada menú dependerá del lugar que ocupe la línea de código en que se crea, como muetra el ejemplo.


Hoy mismo he conseguido hacer lo propio con un menú emergente. Supón que quieres hacer un menú emergente (contextMenu o PopupMenu en VB6) dentro de un control y quieres crear items con sus índices y crear además separadores y subítems para los items. Aquí dejo el código de ejemplo:



CREAR UN MENÚ EMERGENTE (contextmenu) DENTRO DE UN CONTROL


-Inserta un ListBox en el form. Este ejemplo usa  el evento ListBox1_MouseDown para mostrar el menú cuando se pulsa el botón derecho del ratón

Código (vbnet) [Seleccionar]

Option Explicit On
Option Strict On
Public Class Form1
   Dim Contextmenu1 As New ContextMenu()
   Dim ContextItems1(3) As MenuItem
   Dim SubItems1(3) As MenuItem

   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

       For numCItm As Integer = 0 To ContextItems1.Count - 1
           Dim name As String = Nothing
           Select Case numCItm
               Case 0 : name = "Item0"
               Case 1 : name = "-"
               Case 2 : name = "Item2"
               Case 3 : name = "Item3"
           End Select
           ContextItems1(numCItm) = New MenuItem(name)
           If numCItm = 2 Then
               For numCSubItm As Integer = 0 To SubItems1.Count - 1
                   Select Case numCSubItm
                       Case 0 : name = "SubItem0"
                       Case 1 : name = "SubItem1"
                       Case 2 : name = "SubItem2"
                       Case 3 : name = "SubItem3"
                   End Select

                   'Añade SubItems al Item 2
                   SubItems1(numCSubItm) = New MenuItem(name) 'Crea SubItemX del Item2
                   ContextItems1(numCItm).MenuItems.Add(SubItems1(numCSubItm)) 'Añade el subItemX
                   'Evento para los SubItems
                   AddHandler ContextItems1(numCItm).MenuItems(numCSubItm).Click, AddressOf SubItems1_Click
               Next
           End If
           'Añade los Items
           Contextmenu1.MenuItems.Add(ContextItems1(numCItm))
           'Evento de los Items
           AddHandler ContextItems1(numCItm).Click, AddressOf ContextItems1_Click
       Next
   End Sub

   Private Sub ContextItems1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
       'Obtiene índice control seleccionado
       Dim Index As Integer = Array.IndexOf(ContextItems1, sender)

       'Muestra el texto del Item en la barra de título
       MessageBox.Show(ContextItems1(Index).Text)
   End Sub
   Private Sub SubItems1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
       'Obtiene índice control seleccionado
       Dim Index As Integer = Array.IndexOf(SubItems1, sender)

       'Muestra el texto del SubItems en la barra de título
       MessageBox.Show(SubItems1(Index).Text)
   End Sub

   Private Sub ListBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
       If e.Button = MouseButtons.Right Then
           Contextmenu1.Show(ListBox1, New System.Drawing.Point(e.Location.X, e.Location.Y))
       End If
   End Sub
End Class


 



Tazmania40

Muy bueno tu código Lekin, he probado lo del separador, no se me había ocurrido puesto que como te comente el control MenuStrip lo cree en modo diseño y sólo la parte que me hacía falta por código, por eso "MnuRepasoSuma" procede de un nombre del MenuStrip (diseño). Si tuviera que programarlo por código todo sería muy extenso porque cambio propiedades de name, font, checked, pongo imágenes de iconos... y creo que eso sería mejor hacerlo en una clase o un módulo que no se muy bien para hacer una array de controles que se utilice para cuando necesitemos llamarlo como antes se hacía con VB6.

Como mencioné solo faltaría aplicar el método Dispose, he leido algo pero veo que no me hace falta aplicarlo porque los objetos los empleo en el Formulario principal y estos se liberan en el archivo "Form1.Designer.vb" al finalizar el Form. Aunque no se si al salir debería llamar al Objeto.Dispose() para liberar por si acaso el GC (Garbage Collector) no los libera bien los recursos.

Si empleara alguna clase o módulo donde genere los objetos veo que si hay que implementar el método Dispose, que la verdad sería otro tema a tratar y extenso, donde las explicaciones de Microsoft las veo muy complicadas de entender y tendría que ponerme a ello, cosa que de momento para mi programa no necsito.

Excelente tus aportaciones y con el menú contextual. Este Visual Basic se parece cada vez más a Visual C, no tengo nada en contra pero para mi pienso que todo lo que se pueda realizar por diseño te ahorras trabajo y si hay que emplear mucho código es mejor crearte tus clases o módulos para no ser repetitivo por tanto no debería haber desaparecido la forma antigua de llamar a los array de controles y eso debería haberse implementado en Visual Basic 2013.

Muchas gracias por todo y mis nuevas preguntas iran al foro net