elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


 


Tema destacado: Usando Git para manipular el directorio de trabajo, el índice y commits (segunda parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013  (Leído 608 veces)
Tazmania40

Desconectado Desconectado

Mensajes: 43


Ver Perfil
Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« en: 17 Enero 2019, 13:12 »

Buenas estoy realizando un programa y me veo atascado en un evento por código, de momento no encuentro nada pero sigo buscando, a ver si alguno me da la solución antes.

Pongo el siguiente ejemplo y es lo mismo que deseo que funcione con las matrices de 2 dimensiones en Visual Basic 2013.

Código
  1. Private Lista(4) As PictureBox
  2.  
  3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  4.        For A = 0 To 4
  5.            Lista(A) = New PictureBox                       ' Creamos una nueva instancia
  6.            Lista(A).Size = New Size(32, 32)                ' Tamaño PictureBox
  7.            Lista(A).BorderStyle = 1                        ' Dibuja borde
  8.            Lista(A).Location = New Point(A * 32 + A, 50)   ' Posicionamos PictureBox
  9.            Me.Controls.Add(Lista(A))                       ' Añadimos al Formulario
  10.            AddHandler Lista(A).Click, AddressOf Evento1    ' Agregamos evento Click
  11.        Next A      
  12. End Sub
  13.  
  14. Private Sub Evento1(sender As Object, e As EventArgs)
  15.        Dim Index As Integer = Array.IndexOf(Lista, sender) ' Obtenemos el índice seleccionado
  16.        MsgBox("Has pulsado " & Index + 1 & " recuadro.")
  17. End Sub
  18.  

El código de arriba muestra 5 controles de tipo PictureBox como un array, puesto que en NET no se
podía realizar mediante diseño, al hacer click en cualquiera de ellos obtenemos el índice del
que hemos pulsado y así podemos utilizar las propiedades de cada uno de ellos.

Siguiendo con el ejemplo, me gustaría lo mismo para una matriz de 2 dimensiones que la dibujo
perfectamente, pero cuando asigno el evento Click no se que emplear para obtener el índice
puesto que Array.IndexOf es para matrices unidimensionales ¿Como puedo obtener el índice de
una matriz de 2 dimensiones, (0,0) (0,1) (0,2).. (1,0)..etc, al hacer click sobre cada recuadro
PictureBox ? Muchas GRACIAS y saludos

Código
  1. Private Matriz(2, 3) As PictureBox                      ' Matriz de 3x4 elementos
  2. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load      
  3.  
  4.        ' Dibujamos matriz bidimensional de 3 filas y 4 columnas
  5.        For F = 0 To 2                                         ' Recorre las Filas
  6.            For C = 0 To 3                                     ' Recorre las Columnas
  7.                Matriz(F, C) = New PictureBox
  8.                Matriz(F, C).Size = New Size(32, 32)
  9.                Matriz(F, C).BorderStyle = 1
  10.                Matriz(F, C).Location = New Point(C * 32 + C, F * 32 + F + 100)
  11.                Me.Controls.Add(Matriz(F, C))
  12.                AddHandler Matriz(F, C).Click, AddressOf Evento2
  13.            Next C
  14.        Next F
  15.    End Sub
  16.  
  17.    Private Sub Evento2(sender As Object, e As EventArgs)
  18.        ' Fallo porque Array.IndexOf  es para matrices de 1 sola dimensión
  19.        Dim Index As Integer = Array.IndexOf(Matriz, sender)
  20.        '...
  21.    End Sub
  22.  
  23.  


« Última modificación: 17 Enero 2019, 13:14 por Tazmania40 » En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.709



Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #1 en: 17 Enero 2019, 23:22 »

al hacer click en cualquiera de ellos obtenemos el índice del
que hemos pulsado y así podemos utilizar las propiedades de cada uno de ellos.

No entiendo tu planteamiento. Si el objetivo realmente es obtener un índice con el que poder obtener acceso al control y usar sus propiedades, entonces la obtención del índice carece de sentido puesto que el objeto sender es una referencia de dicho control, pudiendo hacer algo así:

Código
  1. Private Sub Evento1(sender As Object, e As EventArgs)
  2.    Dim pcb As PictureBox = DirectCast(sender, PictureBox)
  3.    ' Hacer aquí lo que quieras con este PictureBox...
  4. End Sub

Para todo lo demás, una forma simple de almacenar datos adicionales (que en este caso ayuden a la identificación del control dentro de un array) sería usar la propiedad Control.Tag que hereda la clase PictureBox, ahí puedes especificar el índice y dimensión del Array o lo que tú quieras al momento de crear la instancia de dicho control en los búcles esos que tienes...

Código
  1. For A = 0 To 4
  2.    ...
  3.    Lista(A).Tag = A ' Índice.
  4.    ...
  5. Next A

Pero como ya digo esto en principio no es necesario, puesto que en el controlador de eventos "Evento1" ya tienes acceso al control mediante el objeto sender.

Saludos.


« Última modificación: 17 Enero 2019, 23:26 por Eleкtro (sin pilas) » En línea


Tazmania40

Desconectado Desconectado

Mensajes: 43


Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #2 en: 18 Enero 2019, 10:10 »

En primer lugar muchas gracias Elektro como siempre. Efectivamente el objeto sender, me fije ayer más tarde podía asignar las propiedades en el momento de hacer clic y que ese PictureBox por ejemplo cambiara de color.

Código
  1. Private Sub Evento2(sender As Object, e As EventArgs)        
  2.        sender.BackColor = Color.Blue
  3.        '.......
  4.    End Sub
  5.  

Aunque lo que requiero realmente es obtener el indice como en array de una dimensión. Si hay alguna forma de pasarle esas 2 coordenadas al evento o que las recoja y ponerlo en una variable matriz de 2 dimensiones
Código
  1. Dim Index As Integer = Array.IndexOf(Lista, sender)
  2. ' Esto mismo con una variable matriz de mismo tipo que la principal y donde la clase Array pueda
  3. ' obtener los 2 indices al igual que hace con 1 solo. Aunque sus métodos solo dicen que es para
  4. ' 1 dimensión. Si hay algo parecido a esta clase y obtenerlo fácilmente.
  5.  

Como bien dices la propiedad Tag podemos guardar esas coordenadas, pero lamentablemente la utilizo para guardar valores. Otra posibilidad es agregar otra propiedad personal al control PictureBox (Como Tag, aunque claro serían otras 2 más Matriz(F, C) guardar F y C) , pero sería muy engorroso y alargaría mucho el código, además de que no se.

No se si en los parámetros del evento click pueden recoger esas 2 coordenadas, tal y como hacemos cuando creamos un procedimiento o función, que también me valdría.

Gracias y saludos
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.709



Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #3 en: 18 Enero 2019, 14:10 »

Como bien dices la propiedad Tag podemos guardar esas coordenadas, pero lamentablemente la utilizo para guardar valores. Otra posibilidad es agregar otra propiedad personal al control PictureBox (Como Tag, aunque claro serían otras 2 más Matriz(F, C) guardar F y C) , pero sería muy engorroso y alargaría mucho el código, además de que no se.

La propiedad Tag es de tipo Object, lo que permite asignarle cualquier tipo de objeto, y esto quiere decir que puedes diseñar una clase como la siguiente, en la que almacenarías los datos que tu quisieras, y luego asignar una instancia de esa clase a al propiedad Tag...

Código
  1. Friend NotInheritable Class TestClass
  2.  
  3.    Public Property TestProperty1 As Object
  4.    Public Property TestProperty2 As Object
  5.  
  6.    Public Sub New()
  7.    End Sub
  8.  
  9. End Class

No hace falta mencionar que en esa clase añadirías la cantidad de propiedades que realmente necesites (donde asignarías los índices del array multidimensional, entre otras cosas que desees), y del tipo que realmente sean, no necesariamente del tipo Object. Y entonces podrías hacer:

Código
  1. For A = 0 To 4
  2.    ...
  3.    Dim obj As New TestClass()
  4.    obj.TestProperty1 = A ' Indice.
  5.    obj.TestProperty2 = asignar otra cosa distinta...
  6.    Lista(A).Tag = obj
  7.    ...
  8. Next A
(o lo mismo pero en el búcle del array bidimensional)

...
Código
  1. Private Sub Evento1(sender As Object, e As EventArgs)
  2.    Dim pcb As PictureBox = DirectCast(sender, PictureBox)
  3.    Dim obj As TestClass = DirectCast(pcb.Tag, TestClass)
  4.    ...
  5. End Sub

¿Eso no te sirve para las intenciones que tengas?. Esto sería más rápido que iterar los elementos de un array para hallar el control en cuestión.

De todas formas, si realmente quieres encontrar el índice del control en un array multidimensional, en este ejemplo bidimensional, tan solo debes recorrer sus dimensiones, y lo puedes hacer de la siguiente manera:

Código
  1. Public Shared Function IndicesOf(Of T)(ByVal [array] As T(,), ByVal value As T) As Integer()
  2.  
  3.    For i As Integer = [array].GetLowerBound(0) To [array].GetUpperBound(0)
  4.        For j As Integer = [array].GetLowerBound(1) To [array].GetUpperBound(1)
  5.            If [array](i, j)?.Equals(value) Then
  6.                Return {i, j}
  7.            End If
  8.        Next j
  9.    Next i
  10.  
  11.    Return Nothing
  12.  
  13. End Function
...
Código
  1. Private Sub Evento1(sender As Object, e As EventArgs)
  2.    Dim indices As Integer() = IndicesOf(Matriz, DirectCast(sender, PictureBox))
  3.    Dim pcb As PictureBox = Matriz(indices(0), indices(1))
  4.    ...
  5. End Sub

Saludos.



EDITO:

Casi se me olvida mostrarte la alternativa que mencionaste de añadirle una segunda propiedad "Tag" a la clase PictureBox...

Es tan simple como heredar la clase PictureBox y declarar una propiedad personalizada:

Código
  1. Public Class CustomPictureBox : Inherits PictureBox
  2.  
  3.    Public Property Tag2 As Object
  4.  
  5. End Class

pero ten en cuenta que esto es un "overkill" puesto que con un propiedad tag es más que suficiente al poderle asignar cualquier tipo de objeto, y este puede ser una clase o estructura que contenga múltiples valores como ya mostré en el ejemplo de arriba. Lo que intento decir es que el pensamiento de "2 siempre es mejor que 1" en realidad resulta algo innecesario por lo que acabo de mencionar, por que la solución óptima no sería crear múltiples propiedades similares a Tag, sino en su lugar crear un tipo pesonalizado donde almacenar múltiples valores, y asignar una instancia de ese tipo a la propiedad Tag.

Y por último, aquí tienes lo mismo pero con una personalización más sofisticada o cuidada para la manipulación de este PictureBox personalizado en el diseñador de forms:

Código
  1. <DisplayName(NameOf(CustomPictureBox))>
  2. <Description("A custom PictureBox control.")>
  3. <DesignTimeVisible(True)>
  4. <DesignerCategory(NameOf(UserControl))>
  5. <ToolboxBitmap(GetType(PictureBox), "PictureBox.bmp")>
  6. <ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Require)>
  7. <ClassInterface(ClassInterfaceType.AutoDispatch)>
  8. <ComVisible(True)>
  9. <DefaultBindingProperty(NameOf(CustomPictureBox.Image))>
  10. <DefaultProperty(NameOf(CustomPictureBox.Image))>
  11. <DefaultEvent(NameOf(CustomPictureBox.Click))>
  12. <Docking(DockingBehavior.Ask)>
  13. <PermissionSet(SecurityAction.Demand, Name:="FullTrust")>
  14. Public Class CustomPictureBox : Inherits PictureBox
  15.  
  16.    <DisplayName(NameOf(CustomPictureBox.Tag2))>
  17.    <Description("Additional user-defined data associated with the object.")>
  18.    <Category("Data")>
  19.    <Browsable(True)>
  20.    <Bindable(True)>
  21.    <DefaultValue(DirectCast(Nothing, Object))>
  22.    <Localizable(False)>
  23.    <TypeConverter(GetType(StringConverter))>
  24.    Public Property Tag2 As Object
  25.  
  26. End Class
« Última modificación: 18 Enero 2019, 14:55 por Eleкtro (sin pilas) » En línea


Tazmania40

Desconectado Desconectado

Mensajes: 43


Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #4 en: 19 Enero 2019, 00:11 »

Muchas gracias Elektro eres un genio, haces que lo difícil parezca fácil y encima expones muchas maneras. Voy analizarlas y te voy escribiendo en este mismo post. No tengo mucho tiempo (tengo 3 niñas y dos son bebes) y me cuesta probar y analizar.

El primero de la clase no lo había pensado de esa manera. He realizado el código y funciona. Lo pongo el pequeño ejemplo

Código
  1. Public Class Form1
  2.    Private Matriz(2, 3) As PictureBox                          ' Matriz de 3x4 elementos
  3.  
  4.    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  5.  
  6.        ' Dibujamos matriz bidimensional de 3 filas y 4 columnas
  7.        For F = 0 To 2                                         ' Recorre las Filas
  8.            For C = 0 To 3                                     ' Recorre las Columnas
  9.                Matriz(F, C) = New PictureBox
  10.                Matriz(F, C).Size = New Size(32, 32)
  11.                Matriz(F, C).BorderStyle = 1
  12.                Matriz(F, C).Location = New Point(C * 32 + C, F * 32 + F + 100)
  13.  
  14.                Dim obj As New TestClass()
  15.                obj.Valor = 0
  16.                obj.F = F
  17.                obj.C = C
  18.                Matriz(F, C).Tag = obj
  19.  
  20.                Me.Controls.Add(Matriz(F, C))
  21.                AddHandler Matriz(F, C).Click, AddressOf Evento2
  22.            Next C
  23.        Next F
  24.    End Sub
  25.  
  26.    Private Sub Evento2(sender As Object, e As EventArgs)
  27.        Dim pcb As PictureBox = DirectCast(sender, PictureBox)
  28.        Dim obj As TestClass = DirectCast(pcb.Tag, TestClass)
  29.        pcb.BackColor = Color.Blue
  30.        MsgBox("Fila y Columna (" & obj.F + 1 & "," & obj.C + 1 & ")")
  31.    End Sub
  32.  
  33.    ''''' CLASE PARA ASIGNAR LAS PROPIEDADES
  34.    Friend NotInheritable Class TestClass
  35.        Public Property Valor As Integer
  36.        Public Property F As Integer
  37.        Public Property C As Integer
  38.  
  39.        Public Sub New() ' Constructor vacío
  40.        End Sub
  41.    End Class
  42.  
  43. End Class
  44.  

En este código la pega que veo es en Tag yo tengo valores que al principio valen 0 y luego les asigno otros al pulsar en cada recuadro que provienen de la propiedad Tag de un Picture principal.
Aqui lo he llamado obj.Valor, podia solucionarlo a continuación del evento
Código
  1. obj.Valor = PicPrincipal.Tag
  2. Matriz(obj.F, obj.C).Tag = obj
  3.  
Vamos enredándolo un poco porque siempre tienes que pasarle todas las propiedades, pero la solución es válida para los índices de la matriz. Una pregunta para las prácticas de buena programación

Friend NotInheritable : porque empleas que la clase sea Friend y este modificador (es la clase base según he leido). He probado con Public (funciona y sin modificador) que siempre realizo cuando creo mis programas, claro dentro de una clase externa. Me imagino que Friend es para que no se pueda acceder externamente, aunque ahi yo suelo emplear Private, siempre he tenido un lio con estas cosas que son sencillas.

A ver si mañana (aunque ya es, jeje) pruebo la función para mi código y así conservo la propiedad Tag con los valores y sería más fácil.

Gracias y saludos
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.709



Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #5 en: 19 Enero 2019, 07:15 »

Friend NotInheritable : porque empleas que la clase sea Friend y este modificador (es la clase base según he leido). He probado con Public (funciona y sin modificador) que siempre realizo cuando creo mis programas, claro dentro de una clase externa. Me imagino que Friend es para que no se pueda acceder externamente, aunque ahi yo suelo emplear Private, siempre he tenido un lio con estas cosas que son sencillas.

Realmente no tiene mucha importancia. El modificador de visibilidad de clase Friend hace que la clase solamente sea accesible desde tu proyecto/executable. Simplemente consideré que era la visibilidad de clase más acertada para tu código, puesto que el modificador Public permite acceder a la clase desde cualquier otra clase y no creo que vayas a necesitar hacer eso.

El modificador de clase NotInheritable (sealed en C#) lo que hace es "sellar" la clase para que no se pueda heredar (usando el keyword Inherits como en la clase del CustomPictureBox), es decir, para que no se pueda usar como una clase base.
Este sellado resulta en una pequeña optimización, así que si no tienes necesidad de heredar una clase entonces no hay problema en sellarla usando NotInheritable, pero tampoco pasa nada por no hacerlo...

Aquí te dejo info acerca de las optimizaciones de usar NotInheritable:

Y las guías de diseño que recomienda Microsoft sobre el sellado de clases:

Ojo, esas recomendaciones de Microsoft parecen estar orientadas a escenarios profesionales (ej. el desarrollo de una API pública), ya que sugieren cosas como: "Sellar una clase porque no se puede pensar en un escenario de extensibilidad no es una buena razón" - pero evidentemente no puede existir un escenario de extensibilidad de tu clase "TestClass" puesto que en principio el código de esa clase sellada es solo para que lo uses tú en tu programa, así que aquí pasa a ser más que una buena razón para sellar la clase, y por ello no debes hacerle mucho caso a ciertas recomendaciones en ese sentido.

Saludos.
« Última modificación: 19 Enero 2019, 09:13 por Eleкtro (sin pilas) » En línea


Tazmania40

Desconectado Desconectado

Mensajes: 43


Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #6 en: 19 Enero 2019, 08:53 »

Muchas gracias Elektro por todo, ya he probado la segunda opción y es la que me voy a quedar (dejo aqui el código) es justo lo que quería, por un lado con el sender obtengo las propiedades de la matriz del PictureBox donde he realizado click para cambiar y por otro lado el índice con tu función que era la pregunta principal, así conservo los datos de mi propiedad Tag que puedo modificar.

Tienes un dominio absoluto de los métodos de las clases, te envidio (sanamente) por tu facilidad sobre la programación en general, yo como ya comente en otros post programo por hobby para realizar pequeños jueguecitos principalmente con VB.NET y XNA, aunque ahora también aprendí algo de C# (traducciones  a vb.net) y GameMaker (v.1.4).

Gracias por la explicación de Friend, como suelo utilizar clases externas siempre pongo public. Siempre veo que sueles utilizar la forma correcta y de buenas prácticas para programar, aunque muchos no lo entendemos y dando consejos de que no utilizar. Miraré esa información a modo consulta. Sobre el tema de modificar un PictureBox o cualquier otro control, creo que es muy extenso y ese tercer método no utilizaré. Saludetes y que pases un buen finde

Código
  1. Public Class Form1
  2.    Private Matriz(2, 3) As PictureBox                          ' Matriz de 3x4 elementos
  3.  
  4.    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  5.  
  6.        ' Dibujamos matriz bidimensional de 3 filas y 4 columnas
  7.        For F = 0 To 2                                          ' Recorre las Filas
  8.            For C = 0 To 3                                      ' Recorre las Columnas
  9.                Matriz(F, C) = New PictureBox
  10.                Matriz(F, C).Size = New Size(32, 32)
  11.                Matriz(F, C).BorderStyle = 1
  12.                Matriz(F, C).Location = New Point(C * 32 + C, F * 32 + F + 100)
  13.                Matriz(F, C).Tag = 0
  14.                Me.Controls.Add(Matriz(F, C))
  15.                AddHandler Matriz(F, C).Click, AddressOf Evento2
  16.            Next C
  17.        Next F
  18.    End Sub
  19.  
  20.    Private Sub Evento2(sender As Object, e As EventArgs)
  21.        Dim indices As Integer() = IndicesOf(Matriz, DirectCast(sender, PictureBox))
  22.        Dim pcb As PictureBox = DirectCast(sender, PictureBox)
  23.        pcb.BackColor = Color.Blue
  24.        MsgBox("Valor Fila: " & indices(0) + 1 & " y Columna: " & indices(1) + 1)
  25.    End Sub
  26.  
  27.    ' Función obtener índice de una Matriz de 2 dimensiones
  28.    Public Shared Function IndicesOf(Of T)(ByVal [array] As T(,), ByVal value As T) As Integer()
  29.  
  30.        For i As Integer = [array].GetLowerBound(0) To [array].GetUpperBound(0)
  31.            For j As Integer = [array].GetLowerBound(1) To [array].GetUpperBound(1)
  32.                If [array](i, j).Equals(value) Then
  33.                    Return {i, j}
  34.                End If
  35.            Next j
  36.        Next i
  37.        Return Nothing
  38.  
  39.    End Function
  40. End Class
  41.  
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.709



Ver Perfil
Re: Obtener indice control seleccionado (Matriz 2 dimensiones) VB2013
« Respuesta #7 en: 19 Enero 2019, 10:02 »

No quiero parecer pedante ni condescendiente ni pesado, pero no hay nada que envidiar, son cosas muy sencillitas. La manipulación de arrays (o jagged arrays) uni o multidimensionales es de las primeras cosas que se aprende a hacer si se estudia siguiendo unos pasos correctos de aprendizaje. Y el resto que he podido mostrarte como la herencia de clases o el uso de tipos genéricos pues son conceptos fundamentales de la programación orientada a objetos.

No te he mostrado nada que no pudieras llegar a hacer por ti mismo con el debido aprendizaje y la posterior práctica, pero como eres un tio de familia pues se entiende que no tengas tiempo o ganas suficiente.

En fin. Un placer ayudar a personas así (de agradecidas).

Saludos.
En línea


Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines