jueves, 13 de febrero de 2014

Clases intercambiables utilizando polimorfismo. (2ª Parte)

Crear la clase EditorDibujos


Este artículo es la continuación del anterior 

Añadiremos un evento a EditorDibujos llamado Salvar que genere un interfaz gráfico que sea un control de usuario con interfaz gráfico y pinte en una matriz de 60x60 pixels esos puntos se salvarán en la instancia CLineaPieza cuando se dispare el evento salvar.

1. Añadir un UserControl al proyecto, llamado EditorDibujos, al crear la clase como UserControl, Visual Studio nos permite generar todo el código sobreescrito que necesitamos para un control de usuario. En último lugar, podemos cambiar la declaración de la clase base para indicar que la clase base es CEditorPieza

2. Abrir el editor de la clase EditorDibujos en el diseñador de formularios y poner la propiedad Size en la ventana de propiedades a 175, 150.

3. Añadir los  controles de la tabla que se muestra a continuación y poner sus propiedades con los valores indicados.


propiedades de  editor piezas


Añadir un campo a EditorDibujos que defina los puntos del dibujo. EditorDibujos el cual mantiene un array de puntos separado, copiado como backup de la instancia CLineaPieza cuando el usuario pulsa el botón salvar.

Private m_puntos() As Point = New Point() {}

 Añadir el siguiente campo  para referirse a la instancia de CLineaPieza reeditando EditorDibujos manteniendo esta referencia y así pudiendo copiar  el nuevo array de puntos después de hacer click en el botón Salvar.

Private m_tipodibujo As CLineaPieza

Añadir el siguiente constructor para tomar un parámetro del objeto CLineaPieza. El constructor debe copiar los puntos del objeto CLineaPieza al objeto EditorDibujos, salvando la referencia al objeto CLineaPieza y asignando el método de dibujo al control PictureBox.

Public Sub New(ByVal tipodibujo As CLineaPieza)
        MyBase.New()
        InitializeComponent()
        ReDim Me.m_puntos(tipodibujo.Point.Length - 1)
        tipodibujo.Punto.CopyTo(Me.m_puntos, 0)
        AddHandler Me.PictureBox1.Paint, AddressOf Me.Dibujar
        m_tipodibujo = tipodibujo
End Sub

botón salvar

Añadir el método Dibujar al control PictureBox .


Public Sub Dibujar(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
        e.Graphics.DrawRectangle(New Pen(Brushes.Black, 1), 0, 0, 60, 60)
        Dim punto As Integer
        For punto = 0 To m_puntos.Length - 2
            Dim one As Point = m_puntos(punto)
            Dim two As Point = m_puntos(punto + 1)
            e.Graphics.DrawLine(Pens.Black, one, two)
        Next
End Sub

Crear el manejador de eventos para el evento mousemove del PictureBox y añadir el siguiente código para mostrar las coordenadas en la etiqueta.

Private Sub pictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        Me.Label1.Text = String.Format("({0}, {1})", e.X, e.Y)
End Sub

Crear el manejador de evento para el evento MouseDown y añadir el siguiente código para añadir un Nuevo punto al dibujo de la pieza y redibujar el PictureBox.

Private Sub pictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        ReDim Preserve m_puntos(m_puntos.Length)
        m_puntos(m_puntos.Length - 1) = New Point(e.X, e.Y)
        Me.Refresh()
End Sub

Hacer doble click sobre el botón salvar para crear el evento del botón y añadir en siguiente código para salvar los puntos de la instancia  CLineaPieza y disparar el evento Salvar. El evento RaiseSaved no debe aparecer en IntelliSense por que la clase base de este punto es UserControl y no CEditarPieza.


Private Sub Salvar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Salvar.Click
           
        m_tipodibujo.Puntos = m_puntos
        MyBase.RaiseSaved(Me, New System.EventArgs())
End Sub

Modificar la declaración de la clase para indicar que la clase deriva de la Clase CEditarPieza en lugar de la clase UserControl.

Public Class EditorDibujos
    Inherits CEditorPieza
End Class

Crear la clase CBitmapPieza


Para crear la clase  CBitmapPieza se debe implementar otra vez un par de clases que derivan de las clases CPieza and CEditarPieza. La clase CBitmapPieza mantiene el nombre del archivo bitmap para la pieza. CBitmapPiezaEditor mantiene una referencia a la instancia CBitmapPieza y una copia del archivo bitmap. Después de usar, seleccionar un nuevo fichero bitmap y pulsar sobre el botón Salvar, el nuevo nombre de fichero se guarda en una instancia de CBitmapPieza.

Añadir una nueva clase al proyecto y llamarla CBitmapPieza.
Modificar la declaración de la clase para indicar que la clase deriva de la clase  CPieza.

Public Class CBitmapPieza
    Inherits CPieza

    Public Overrides Function Clone() As CPieza

    End Function

    Public Overrides Sub Dibujar(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)

    End Sub

    Public Overrides Function ObtenerEditor() As CEditorPieza

    End Function
End Class

Añadir el siguiente campo y propiedad para almacenar el nombre de fichero del bitmap:

Private m_piezabitmap As String = ""
Public Property BitmapFile() As String
        Get
            Return (m_piezabitmap)
        End Get
        Set(ByVal Value As String)
            m_piezabitmap = Value
        End Set
End Property.

Definir el método Dibujar, exactamente como el de la clase CLineaPieza, la codificación del interfaz de usuario debe usar este método para mostrar la pieza.

Public Overrides Sub Dibujar(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
        e.Graphics.DrawImage(New _
        System.Drawing.Bitmap(m_piezabitmap), 0, 0)
End Sub

Definir el método ObtenerEditor. Saldrá un error en este punto  porque aún no se he implementado la clase CBitmapPiezaEditor.

Public Overrides Function ObtenerEditor() As CEditorPieza
        Return New CBitmapPiezaEditor(Me)
End Function

Definir el método Clone.

Public Overrides Function Clone() As CPieza
        Dim newTipodibujo As New CBitmapPieza()
        newTipodibujo.BitmapFile = Me.BitmapFile
        Return (newTipodibujo)
End Function

 

Crear la claseCBitmapPiezaEditor

La clase CBitmapPiezaEditor necesita solo buscar y guardar. Botones salvar  y un picturebox para mostrar los archivos bitmap.

Añadir una nueva clase UserControl al proyecto. Llamarla CBitmapPiezaEditor.

Abrir CBitmapPiezaEditor en el diseñador y poner la propiedad Size a 175, 150 en la ventana de propiedades.

Añadir los siguientes controles y poner sus propiedades como las mostradas en la tabla.

propiedades editor bitmap

Abrir la clase CBitmapPiezaEditor con el editor de código y añadir un campo al archivo bitmap. La clase CBitmapPiezaEditor mantiene una referencia separada al nombre de archivo que se copió en la instancia de CBitmapPieza cuando el usuario pulsa el botón Salvar.

Private m_ficheroBitmap As String

Añadir un campo para referirse a la instancia CBitmapPieza. CBitmapPiezaEditor mantiene esta referencia así que copia el nombre del archivo bitmap en CBitmapPieza cuando el usuario pulsa el botón salvar.

Private m_tipodibujo As CBitmapPieza

Añadir el siguiente constructor, el cual toma un parámetro, una instancia de  CBitmapPieza.
El constructor copiará el nombre de archivo de CBitmapPieza a CBitmapPiezaEditor, salvando la referencia a CLineaPieza y asignando un método de dibujo al control PictureBox.

Public Sub New(ByVal tipodibujo As CBitmapPieza)
MyBase.New()
      InitializeComponent()
      m_tipodibujo = tipodibujo
      m_piezabitmap = tipodibujo.BitmapFile
      AddHandler Me.PictureBox1.Paint, AddressOf Me.Dibujar

End Sub


botones buscar y salvar

Añadir el método dibujar.

Public Sub Dibujar(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
        e.Graphics.DrawImage(New Bitmap(m_piezabitmap), 0, 0)
End Sub

Crear el manejador de evento para el botón buscar y añadir este código para mostrar la caja de dialogo.

Private Sub Buscar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Buscar.Click
        OpenFileDialog1.ShowDialog()
        If (Me.OpenFileDialog1.FileName.Length <> 0) Then
            m_piezabitmap = Me.OpenFileDialog1.FileName
            Me.PictureBox1.Refresh()
        End If
End Sub

Crear el manejador del evento Click del botón salvar y añadirle este código para salvar en fichero y guardarlo en una instancia de CBitmapPieza al ejecutar el evento salvar.

Private Sub Salvar_Click(ByVal sender As System.Object,ByVal e As System.Eve ntArgs) Handles Salvar.Click
        m_tipodibujo.BitmapFile = m_piezabitmap
        MyBase.RaiseSaved(Me, New System.EventArgs())
End Sub

 

El Interfaz de usuario


La implementación para dibujar la pieza como bitmap es diferente, sin embargo el código del interfaz de usuario, es muy simple y no revela las diferencias entre los dos tipos de piezas dibujadas.

Crear los elementos del interfaz de usuario


El interfaz de usuario contiene paneles para plantillas y para piezas editadas y un área para editar piezas.

Abrir el diseñador del formulario form1, en la ventana de propiedades, cambiar la propiedad Size del form1 y ponerla a 344,392.

Añadir los siguientes controles al formulario.

propiedades interfaz de usuario

Con lo que el interfaz de usuario queda así:

interfaz

La clase  CPieza muestra cada pieza gracias al  método Dibujar, y  no contiene ningún tipo de elemento que pueda mostrar en un formulario, tal y como un control panel con una imagen o un PictureBox.

Añadir la siguiente mini clase, BotonPieza, después del final la clase Form1. Este control de usuario creado por el cliente se usa para mostrar las piezas en plantillas y en paneles de piezas.

Public Class BotonPieza
    Inherits UserControl

    Private m_pieza As CPieza
    Public Sub New(ByVal nuevaPieza As CPieza)
        Me.Size = New Size(61, 61)
        m_pieza = nuevaPieza
        AddHandler Me.Paint, AddressOf nuevaPieza.Dibujar
    End Sub

    Public Property Pieza() As CPieza
        Get
            Return (m_pieza)
        End Get
        Set(ByVal Value As CPieza)
            m_pieza = Value
        End Set
    End Property
End Class

Hay que notar que le uso del método Dibujar de CPieza es como el método Paint del control. En definitiva consiste en añadir una pieza a la instancia como una propiedad del control.

Crear las instancias de la plantilla


Las plantillas de las piezas son instancias de cada clase CLineaPieza y CBitmapPieza que se muestran en el control de usuario BotonPieza. Las instancias de BotonPieza se añaden al panel de plantillas.

Esta es sólo la parte del código de la interfaz de usuario que necesita para crear  la clase CPieza. No hay necesidad de agregar más de una  instancia de CBitmapPieza en el panel de plantillas. Es mejor añadir múltiples instancias de CLineaPieza, ya que el usuario puede guardarlas para cuando tenga que volver a crear dibujos con una  base común. Si se extiende la aplicación para añadir más tipos de piezas, se necesitaría este código para modificarlo. El resto de la aplicación se ocupará de las instancias CLineaPieza y  CBitmapPieza  usando las referencias de la clase base CPieza.

Hacer doble click en el diseñador del formulario para crear el evento Form_Load y añadir el siguiente código al evento para añadir instancias de CPieza al panel de plantilla.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Dibujo1 As New CLineaPieza()
        Dibujo1.Puntos = New Point() {New Point(0, 30), New Point(60, 30), New Point(60, 0), New Point(30, 0), New Point(30, 60)}

        Dim Dibujo2 As New CLineaPieza()
        Dibujo2.Puntos = New Point() {New Point(30, 0), New Point(60, 30), New Point(30, 60), New Point(0, 30), New Point(30, 0), New Point(0, 0)}

        Dim bitmap1 As New CBitmapPieza()
        bitmap1.BitmapFile = "CarpetaProyecto\CaballoBlanco.bmp"

        Dim tipodibujos() As CPieza = {Dibujo1, bitmap1, Dibujo2}
        Dim pt As Integer
        For pt = 0 To tipodibujos.Length - 1
            Dim button As New BotonPieza(tipodibujos(pt))
            button.Top = 70 * pt
            button.Left = 5
            AddHandler button.Click, AddressOf Me.Plantilla_Click
            Me.Plantillas.Controls.Add(button)
        Next
End Sub

 

Editar y salvar las nuevas piezas


El uso de las instancias de  CLineaPieza y CBitmapPieza se hace a través de las variables de referencia de CPieza. Como se ha dicho antes se simplifica la programación gracias al uso de polimorfismo, reduciendo el número de nombres de clase con las que hay que trabajar Si hay que añadir otros tipos de pieza,  no hay que cambiar el código.

A continuación se añade el siguiente código al formulario al método Pieza_Click y se añade un  campo para referirse a la nueva instancia CPieza. Hay que tener en cuenta que da igual en  qué plantilla se ha hecho click, o si el  tipo de esa instancia es CLineaPieza o CBitmapPieza. Porque CEditarPieza deriva de UserControl, no importa si la instancia, devuelve una instancia de EditorDibujos o  CBitmapPiezaEditor.

Private m_nuevaPieza As CPieza = Nothing
Private Sub Pieza_Click(ByVal sender As Object, ByVal e As System.EventArgs)

        Dim button As BotonPieza = CType(sender, BotonPieza)
        m_nuevaPieza = button.Pieza.Clone()
        Dim designer As CEditorPieza = m_nuevaPieza.ObtenerEditor()
        designer.Location = New Point(10, 10)
        Me.Editor.Controls.Add(designer)
        AddHandler designer.Saved, AddressOf Me.PiezaSalvada
End Sub

Finalmente añadiremos el siguiente código para el método  PiezaSalvada. Esto agrega una pieza en el panel de piezas. Una vez que la pieza se guarda, el control de CEditarPieza no tiene ningún propósito. Con lo que se desecha de modo que no utiliza más recursos del sistema. Debido a que controlamos el código, sabemos que el parámetro enviado  en el evento PiezaSalvada es una instancia de CEditarPieza. Esto envía un parámetro que se puede controlar con un cast.

Private Sub PiezaSalvada(ByVal sender As Object, ByVal e As EventArgs)
        Me.Controls.Remove(CType(sender, Control))
        CType(sender, Control).Dispose()
        Dim pb As BotonPieza = New BotonPieza(m_nuevaPieza)
        pb.Left = Me.Piezas.Controls.Count * 70
        pb.Top = 5
        pb.Enabled = False
        Me.Piezas.Controls.Add(pb)
End Sub

Todo lo que necesita el código del interfaz de usuario es una pequeña clase y tres controladores de eventos. Gran parte del trabajo se mete en las clases CPieza y CEditarPieza.


 Para probar la aplicación presionar F5 para ejecutar. El siguiente gráfico muestra los resultados  después de añadir nuevos modelos de piezas.

editor piezas


No hay comentarios:

Publicar un comentario