Como crear un array de controles desde un módulo dentro de un control (Panel)

Iniciado por Lekim, 22 Septiembre 2015, 19:36 PM

0 Miembros y 2 Visitantes están viendo este tema.

Lekim

Hola amigos

Hasta ahora he estado usando Windows Forms, pero para conseguir un aspecto gráfico más bonito en mi aplicación he decidido usar WPF. El problema es que cambian algunos eventos y propiedades y otras cosas y tengo que ir investigando.

Me ha pasado que ya tenía una apliación hecha en Windows Forms y he ido pasado el código a WPF pero resulta que concretamente esto no lo he sabido resolver.

En Windows Forms creé un Panel y un array de controles con Labels. El panel se crea desde un módulo que he llamado (modCrearPanel).  El módulo también tiene eventos y más cosas pero no viene a cuento. La cuestión es poder crear los Labels dentro de un control StackPanel en WPF.


- Primero: Creo un Panel o StackPanel en el Windows Forms y en MainWindow en WPF
- Segundo paso:

EN WINDOWS FORMS
(no es el código original, es una simplificación)
Código (vbnet) [Seleccionar]

Module Module1
   Dim Etiqueta As New Label
   Dim lblEtiqueta(4) As Label
   Sub CrearPanel()
       Dim Y As Integer = 20
       Dim X As Integer
       Form1.Panel1.BackColor = Color.DarkGray
       X = 10 'valor X inicial
       For I as Integer = 0 To 3
           lblEtiqueta(I) = New Label
           lblEtiqueta(I).Width = 15
           lblEtiqueta(I).Height = 15
           lblEtiqueta(I).BackColor = Color.Gray
           lblEtiqueta(I).Location = New Point(X, Y) 'posición X e Y del cuadro
           Form1.Panel1.Controls.Add(lblEtiqueta(I)) 'Crea el label
           X = X + 20
       Next
   End Sub
End Module


CON WPF (NO FUNCIONA)
Código (vbnet) [Seleccionar]
Module Module1
   Dim Etiqueta As New Label
   Dim lblEtiqueta(4) As Label
   Dim Main As MainWindow
   Public Sub CrearPanel()
       Dim Y As Integer = 10
       Dim X As Integer
       Main.StackPanel1.Background = Brushes.DarkGray
       X = 10
       For i As Integer = 0 To 3
           lblEtiqueta(i) = New Label
           lblEtiqueta(i).Background = Brushes.Gray
           lblEtiqueta(i).Width = 15
           lblEtiqueta(i).Height = 15
           lblEtiqueta(i).Margin = New Thickness(0, X, Y, 0)
           X = X + 20
           Main.StackPanel1.Children.Add(lblEtiqueta(i))
       Next i
       Main.Show()
   End Sub
End Module



Para crear el Penel se llama desde el Load o Loaded en WPF a CrearPanel.

Con toda seguridad el problema está en la forma de poder hacer referencia al StackPanel1 que se encuentra en MainWindow. No se como se hace.


Gracias por ayuda




Encontré la forma   :laugh:


Código (vbnet) [Seleccionar]
Module Module1
   Dim Etiqueta As New Label
   Dim lblEtiqueta(4) As Label
   Private app As Application
   Private Main As MainWindow
   Public Sub CrearPanel()
       Dim Y As Integer = 10
       Dim X As Integer
       app = System.Windows.Application.Current
       Main = CType(app.MainWindow, MainWindow)

       Main.StackPanel1.Background = Brushes.DarkGray
       X = 10
       For i As Integer = 0 To 3
           lblEtiqueta(i) = New Label
           lblEtiqueta(i).Background = Brushes.Gray
           lblEtiqueta(i).Width = 15
           lblEtiqueta(i).Height = 15
           lblEtiqueta(i).Margin = New Thickness(0, X, 0, Y)
           X = X + 20
           Main.StackPanel1.Children.Add(lblEtiqueta(i)) 'Crea el label
       Next i
   End Sub
End Module




Esto es lo que había que añadir al código para poder hacer referencia a objetos creados en MainWindow

Código (vbnet) [Seleccionar]

   Private app As Application
   Private Main As MainWindow
   Public Sub CrearPanel()
       app = System.Windows.Application.Current
       Main = CType(app.MainWindow, MainWindow)
      Main.StackPanel1.Background = Brushes.DarkGray
End Sub


Encontré la solución aquí:
https://msdn.microsoft.com/es-es/library/vstudio/ms750944%28v=vs.100%29.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

Ahora que lo he conseguido hacer solo falta colocar correctamente los labels ya que en susitución a "Location"  he usado "Margin" y no funciona igual. Veré a ver  :-\

Saludos


Eleкtro

Respecto al código que has mostrado para WindowsForms, yo evitaría el uso de un módulo en el cual supuestamente defines eventos y los disparas o pones en escucha algún event-handlers despreocupándote de su tiempo de vida, donde además declaras objetos (controles) que probablemente nunca liberarás maunálmente (hasta finalizar la app), pues yo lo haría más o menos así ...dentro de lo que cabe para este ejemplo en particular, en una Class que implementa la interfáz IDisposable:

Código (vbnet) [Seleccionar]
Public Class TestClass : Implements IDisposable

   Private ReadOnly lbl As Label
   Private ReadOnly lblArray(3) As Label
   Private ReadOnly pnl As Panel

   <DebuggerStepThrough>
   Public Sub New(ByVal pnl As Panel)
       Me.pnl = pnl
       Me.lbl = New Label

       For index As Integer = 0 To (Me.lblArray.Length - 1)
           Me.lblArray(index) = New Label
       Next index

       Me.CrearPanel()
   End Sub

   Private Sub New()
   End Sub

   Private Sub CrearPanel()

       Dim startPos As New Point(x:=10, y:=20)
       Dim margin As Integer = 20

       Me.pnl.BackColor = Color.DarkGray

       For Each lbl As Label In Me.lblArray

           With lbl
               .Width = 15
               .Height = 15
               .BackColor = Color.Gray
               .Location = startPos 'posición X e Y del cuadro
               startPos.X += margin
           End With

       Next lbl

       If Not Me.pnl.InvokeRequired Then
           Me.pnl.Controls.AddRange(Me.lblArray)
       Else
           Me.pnl.Invoke(Sub() Me.pnl.Controls.AddRange(Me.lblArray))
       End If

   End Sub

#Region "IDisposable Support"

   ' To detect redundant calls when disposing.
   Private isDisposed As Boolean = False

   ''' <summary>
   ''' Releases all the resources used by this instance.
   ''' </summary>
   Public Sub Dispose() Implements IDisposable.Dispose
       Me.Dispose(isDisposing:=True)
       GC.SuppressFinalize(obj:=Me)
   End Sub

   Protected Sub Dispose(ByVal isDisposing As Boolean)

       If Not Me.isDisposed Then

           If isDisposing Then

               ' Dispose managed objects here...

               If Me.lbl IsNot Nothing Then
                   Me.lbl.Dispose()
               End If

               For Each lbl As Label In Me.lblArray
                   If lbl IsNot Nothing Then
                       lbl.Dispose()
                   End If
               Next lbl

           End If

       End If

       Me.isDisposed = True

   End Sub

#End Region

End Class


Modo de empleo:
Código (vbnet) [Seleccionar]
Dim cosa As New TestClass(Me.Panel1)
' cosa.Dispose()





Sobre WPF, asumiré que el control que has elegido cómo equivalente de un panel es un Canvas, en caso contrario hazlo (es lo recomendable por la MSDN).

La respuesta corta es que debes asignar el margen del control con el uso de la propiedad Margin:
Código (vbnet) [Seleccionar]
Label.Margin = New Thickness(Point.X, Point.Y, 0, 0)

También puedes utilizar las funciones Canvas.SetLeft y Canvas.SetTop para asignar el margen de un control hijo:
Código (vbnet) [Seleccionar]
Canvas.Children.Add(Label)
Canvas.SetLeft(label, Point.X)
Canvas.SetTop(label, Point.Y)


La respuesta larga es que, si te sientes más agusto con la familiaridad de la tecnología WindowsForms, entonces podrías heredar un label para definir la propiedad personalizada Location y usarla cómo la solias usar antes:

Código (vbnet) [Seleccionar]
Public Class CustomLabel : Inherits Label

   Public Property Location() As Point
       Get
           Return New Point(MyBase.Margin.Left, MyBase.Margin.Top)
       End Get
       Set(ByVal value As Point)
           ' La lógica del método "Set" se podría mejorar para que realmente se pueda considerar un "Location".
           MyBase.Margin = New Thickness(value.X, value.Y, 0, 0)
       End Set
   End Property

End Class


Y este código sería el equivalente para WPF de la class de WinForms que mostré arriba, donde aquí como puedes ver no implemento la interfáz IDisposable, ya que a diferencia de WinForms, la mayoría de controles realmente no tienen ningún recurso que liberar.

Código (vbnet) [Seleccionar]
Public NotInheritable Class TestClass

   Private ReadOnly lbl As Label
   Private ReadOnly lblArray(3) As Label
   Private ReadOnly pnl As Canvas

   <DebuggerStepThrough>
   Public Sub New(ByVal pnl As Canvas)
       Me.pnl = pnl
       Me.lbl = New Label

       For index As Integer = 0 To (Me.lblArray.Length - 1)
           Me.lblArray(index) = New Label
       Next index

       Me.CrearPanel()
   End Sub

   Private Sub New()
   End Sub

   Private Sub CrearPanel()

       Dim startPos As New Point(x:=10, y:=20)
       Dim margin As Integer = 20

       Me.pnl.Background = Brushes.Gray

       For Each lbl As Label In Me.lblArray

           With lbl
               .Width = 15
               .Height = 15
               .Background = Brushes.Red
               .Margin = New Thickness(startPos.X, startPos.Y, 0, 0)

               startPos.X += margin
           End With

           Me.pnl.Children.Add(lbl)

       Next lbl

   End Sub

End Class


Modo de empleo:
Código (vbnet) [Seleccionar]
Dim cosa As New TestClass(Me.Canvas1)

Saludos








Lekim

Gracias muy buena respuesta, me has resuelto todas las dudas.;-)

Y lo del canvas es lo que buscaba, creía que el equivalente en WPF era el stackpanel  :P

Eleкtro

Cita de: Lekim en 23 Septiembre 2015, 15:54 PMY lo del canvas es lo que buscaba, creía que el equivalente en WPF era el stackpanel  :P

Esto te podría servir de ayuda para futuras dudas respecto a otros controles...

Windows Forms Controls and Equivalent WPF Controls - MSDN

Saludos!








Lekim

Ok, gracias Elektro

Por curiosidad he probado de hacer algo parecido con el código que expuse al principio. A ver que te parece  ::)


Código (vbnet) [Seleccionar]

Module Module1
    Dim lblEtiqueta(4) As Label
    Sub CrearPanel(ByVal pnl As Object, ByVal Disponible As Boolean)
        Dim Starlbl As New Point(x:=10, y:=20)
        pnl.backcolor = Color.DarkGray
        If Disponible Then

            For I As Integer = 0 To 3
                lblEtiqueta(I) = New Label
                lblEtiqueta(I).Width = 15
                lblEtiqueta(I).Height = 15
                lblEtiqueta(I).BackColor = Color.Gray
                lblEtiqueta(I).Location = Starlbl 'posición X e Y del cuadro
                pnl.Controls.Add(lblEtiqueta(I)) 'Crea el label
                Starlbl.X += 20
            Next
        Else
            Try
                For Index As Integer = 0 To 3
                    lblEtiqueta(Index).Dispose()
                Next Index
            Catch ex As Exception
            End Try
        End If
    End Sub
End Module




Para llamar uso:
(para mostrar los Labels)
Código (vbnet) [Seleccionar]
CrearPanel(Panel1, True)

(para quitar los labels)
Código (vbnet) [Seleccionar]
CrearPanel(Panel1, False)