viernes, 30 de agosto de 2013

Trabajar con métodos

Este artículo es la continuación de la implementación de un Primer Programa Orientado a Objetos. Aquí veremos cómo crear métodos de la clase, cómo sobrecargarlos y cómo rellenar un ListBox utilizando el objeto ArrayList de .NET. También veremos como agregar un control a un formulario en tiempo de ejecución. Finalmente utilizaremos los métodos para arrastrar y soltar objetos dentro de un formulario.

Consideraciones en el diseño de clases reutilizables


La idea de la POO es hacer que nuestro código pueda ser reutilizable o incluso exportable a otros programas a través del conocido Copiar y Pegar, para hacer esto posible hay que tener en cuenta algunos conceptos importantes:


Abstracción


Cuando se crea una clase, existe la tentación de agregar una gran cantidad de funciones de apoyo, no obstante hay que tener en cuenta que cada método, propiedad o evento que se añada a la clase va a limitar las opciones de reutilización de la clase. También es necesario saber que una clase debe hacer una cosa bien. Si utilizamos una abstracción clara, será más fácil para los desarrolladores, pues sus métodos y propiedades tendrán sentido y su comportamiento será más predecible.


Contención


Una clase es una especie de caja negra donde se almacena un tipo de objeto con datos diversos y los métodos que trabajan con ellos, por ejemplo La clase CHospital puede almacenar Nombres, direcciones, Números de habitación, Historias clínicas o cualquier otra cosa. Por lo tanto, hay que tener cuidado de no hacer ninguna suposición sobre el contexto de la clase. A pesar de que se va a desarrollar una clase en un contexto de una aplicación de Windows, la clase no debe contener ninguna referencia a un formulario.

Es posible que se desee añadir un comportamiento que dependa del código de cliente. Se podría añadir un método que tome un argumento para especificar una forma de tratar la infoirmación. En este caso es mejor agregar un método que cree un objeto específico de .NET
De tal forma que sea utilizable a través de una serie de códigos de cliente más amplia.

Por ejemplo, si se desea agregar código que muestre un dibujo de un hospital para cada hospital agregado. Se puede añadir un método que cree un objeto de .NET Graphics. Esto nos va permitir escribir código para dibujar el hospital en cualquier objeto que contenga un objeto Graphics de .NET.

Código Cliente


Si la clase está bien diseñada, el código de cliente se simplificará mucho, las estructuras de decisión y los bucles estarán contenidos en los métodos de la clase, de este modo no aparecerán en el código de cliente que se limitará a llamadas a los métodos de las clases.


Interfaz


Hay que proporcionar una interfaz completa, pero sin exajerar. Implementar el interfaz lo suficientemente bien como para que un posterior desarrollador  pueda extenderla. Por ejemplo, la clase CHospital tiene un método para dar de alta a un Médico en un hospital y otro para Ubicar un paciente en una sala, pero posteriormente se puede seguir completando, añadiéndo metodos por ejemplo para asignar una especialidad a un médico o un paciente a un médico.


A continuación continuamos añadiendo funcionalidad a la Clase CHospital, en este caso agregaremos un constructor y más métodos.

métodos el la programación orientada a objetos



Crear los constructores


 Añadir el siguiente constructor. Los telefonos estarán contenidos en un objeto Array

Private m_telefonos As New System.Collections.ArrayList()

Public Sub New(ByVal telefonos() As CHospital)
      m_telefonos.AddRange(telefonos)
End Sub

Crear un método privado


Añadir el siguiente método privado en la clase CPersona para asignar una especialidad a un médico.

Private Function AsignaMedicoEsp(ByVal Nom_Medico As CPersona, ByVal Especialidad As String) As Boolean

Dim Reg As RDO.rdoResultset
Dim strSQL As String

Reg = Nothing

On Error GoTo Errores_AsignaMedicoEsp
strSQL = "INSERT INTO dbo.tbMedEsp (strId_MED,strID_ESP) VALUES (" & Nom_Medico.Medico & "," & Especialidad & ")"

      glbBaseDatos.Execute(strSQL)
      Reg.Close()
      Return True

Errores_AsignaMedicoEsp:
      Return False
End Function


Cómo crear métodos sobrecargados


Añadir el método AsignaPerEsp a la clase CPersona para asignar una especialidad/dolencia a un médico/paciente. Si ambos métodos llevan el mismo número de argumentos, estos no pueden coincidir en los tipos. En este caso la especialidad del médico se introduce como un string y la dolencia se introduce como un integer que luego se traduce a un string.

La función va precedida de la palabra clave Overloads, pero no es imprescindible, puede omitirse la palabra Overloads.


Public Overloads Function AsignaPerEsp(ByVal Codigo As String, ByVal Especialidad As String) As Boolean

Dim Reg As RDO.rdoResultset
Dim strSQL As String

      Reg = Nothing

On Error GoTo Errores_AsignaPerEsp
      strSQL = "INSERT INTO dbo.tbMedEsp (strId_MED,strID_ESP) VALUES (" & Codigo & "," & Especialidad & ")"

      glbBaseDatos.Execute(strSQL)
      Reg.Close()
      Return True

Errores_AsignaPerEsp:
      Return False
    End Function


Public Overloads Function AsignaPerEsp(ByVal Codigo As String, ByVal Dolencia As Integer) As Boolean

Dim Reg As RDO.rdoResultset
Dim strSQL As String
Dim strDolencia As String

Reg = Nothing

On Error GoTo Errores_AsignaPerEsp

      Select Case Dolencia
            Case 1
                strDolencia = "Traumatología"
            Case 2
                strDolencia = "Aparato digestivo"
      End Select

      strSQL = "INSERT INTO dbo.tbMedEsp (strId_MED,strID_ESP) VALUES (" & Codigo & "," & strDolencia & ")"

        glbBaseDatos.Execute(strSQL)
        Reg.Close()
        Return True
Errores_AsignaPerEsp:
        Return False
End Function

Testear los métodos sobrecargados


Para testar los métodos, basta con crear una instancia de la clase y hacer uso de ellos dependiendo de cual se necesite. El programa distiguirá cual utilizar por los argumentos.

Public Shared Sub main()

Dim m_persona As CPersona = New CPersona

      m_persona.AsignaPerEsp("MED01", "Traumatología")
      m_persona.AsignaPerEsp("PAC01", 1)

End Sub

Esté metodo puede declararse en el formulario y ser llamado desde el Form_load del formulario con main() o tambien en una clase, por ejemplo en CPersona y ser llamado desde el Form_Load del formulario como:

Dim m_persona As CPersona = New CPersona
CPersona.main()

Antes de ir más lejos, es una buena idea para probar las nuevas clases antes de
su integración con el resto del programa.

Agregar un poco de código de prueba antes de terminar la clase tiene ventajas, pues
no entorpece la lectura del resto del programa. Dado que el código está en la clase, no está en la aplicación principal.

Cuando se realizan cambios en una clase, ES MUY IMPORTANTE probar de nuevo cada propiedad y cada método, el constructor y cualquier conducta que se base en un límite. Por ejemplo, probar a superar los límites numéricos de los parámetros o intentar recuperar valores que no existen.  Tambien habrá que probar cualquier conducta que se base en una decisión. Si tenemos un método que hace dos cosas diferentes dependiendo de si el tercer parámetro es verdadero o falso, probar el método con diferentes combinaciones de parámetros


En el caso de que elijamos colocar el método
Main() en una clase, utilizaremos el identificador Shared, estos métodos son miembros de la clase y no están asociados a ninguna instancia particular. Pueden ser incluso llamados antes de crear cualquier instancia de la clase.  Además desde esté metodo se tiene acceso a los miembros privados de la clase.

Cómo utilizar el objeto ArrayList de .NET para cargar un ListBox


ArrayList es un tipo de colección proporcionado por el .NET.  Estas colecciones son potentes porque pueden contener cualquier tipo de objeto. Pero tienen la desventaja de que no son de tipo seguro, una colección podría contener varios tipos diferentes de objetos. Cuando se lanza el objeto que se recupera de la colección, estamos contando con que sea de un tipo particular. En otro artículo posterior veremos cómo se puede especializar la clase ArrayList (u otra  clase de colección) usando herencia para garantizar que sólo se añade o elimina un tipo de objeto.

Para usar el ArrayList por ejemplo para rellenar un ListBox con el nombre de los hospitales.

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

'Conexión a la base de datos
Dim dt As New DataTable
Dim Valor As String
CadConexion = "User ID=sasorolla;Initial Catalog=model;Data Source=SRV-SOROLLA\SOROLLA2005;Password=7gfyfyrj"
       
Conexion.conectar(CadConexion)

Dim daDB As New SqlClient.SqlDataAdapter("SELECT * FROM dbo.tbHospital", Conexion.cnDB)

Dim i As Integer
Dim myAL As New ArrayList()
       
daDB.Fill(dt)
i = dt.Rows.Count

For i = 0 To dt.Rows.Count - 1
      Valor = dt.Rows(i).Item(2)  'Nombre del hospital
      myAL.Add(Valor) 'rellenado de un ArrayList
Next
      MostrarValores(myAL)
End Sub

Añadimos al formulario un ListBox llamado ListHospitales y creamos el siguiente procedimiento.

Public Sub MostrarValores(ByVal myList As IEnumerable)
Dim obj As Object

For Each obj In myList
      ListHospitales.Items.Add(obj)
Next obj

End Sub


Cómo añadir un control en tiempo de ejecución


Vamos a añadir un botón en tiempo de ejecución. Para ello en el Form_Load se añade el siguiente código:

'añadir un botón en tiempo de ejecución
Dim BotonPru As New Button()
Me.Controls.Add(BotonPru)
AddHandler (BotonPru.Click), AddressOf BotonPru_Click

Ahora aparece un nuevo botón sobre el formulario en la esquina superior izquierda, si queremos ubicarlo correctamente y darle una posición definida

BotonPru.Left = 45
BotonPru.Top = 46

Para añadir funcionalidad al botón basta con crear el evento BotonPru_Click y colocar el Código en él.

Private Sub BotonPru_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        MsgBox("Hola Mundo")
End Sub

Para cada evento que queramos habilitar hay que crear un línea con la llamada al evento

AddHandler (BotonPru.Click), AddressOf BotonPru_Click

Si queremos por ejemplo reubicar  con el ratón el control haríamos lo siguiente:

La definición del control la sacamos fuera del Form_Load

Dim BotonPru As New Button()  

Dentro del Form_Load hacemos

Me.Controls.Add(BotonPru)
'Definimos los eventos que serán activos
AddHandler (BotonPru.Click), AddressOf BotonPru_Click
AddHandler (BotonPru.MouseDown), AddressOf BotonPru_MouseDown
AddHandler (BotonPru.MouseMove), AddressOf BotonPru_MouseMove
AddHandler (BotonPru.MouseUp), AddressOf BotonPru_MouseUp

Y en la cabecera definimos un variable que controle si deseamos o no mover el control.

Dim isDown As Boolean = False

'Si pulsamos el botón del ratón dentro del control (botón) habilita la variable para que pueda moverlo
Private Sub BotonPru_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)

      If e.Button = Windows.Forms.MouseButtons.Left Then
            isDown = True
      End If

End Sub

'define getPoint
Protected Function getPoint(ByVal e As Windows.Forms.MouseEventArgs, ByVal Ctl As Windows.Forms.Control) As Point

Dim punto As New Point(Ctl.Left \ 2 + e.X, Ctl.Top \ 2 + e.Y)
      Return punto
End Function

'toma la posición del ratón y coloca el botón
Private Sub BotonPru_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)

      If isDown = True Then
            BotonPru.Location = getPoint(e, BotonPru)
            Me.Cursor = Cursors.SizeAll
      End If
End Sub
'Si soltamos el botón del ratón dentro del control (botón) deshabilita la variable para que no pueda moverlo
Private Sub BotonPru_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
      
      isDown = False
 End Sub

Ejecutar operaciones arrastrar y soltar (drag-and-drop).
Un evento arrastrar y soltar (Drag and Drop) en realidad es el mismo que cortar y pegar con el ratón en lugar del teclado. En ambos casos, se tiene una fuente (donde se encuentra) y un objetivo (en el que está pegando a). Ya sea durante la operación, se mantiene en la memoria una  copia de los datos. Cortar y pegar utiliza el Portapapeles, En arrastrar y soltar se utiliza un objeto DataObject, que es en esencia un portapapeles privado.
Esta es la secuencia de eventos típica de un evento arrastrar y soltar:

1. Arrastrar se inicia con una llamada al método
DoDragDrop para el control de código fuente.

El método DoDragDrop toma dos parámetros:
• Los datos, especificando los datos a transmitir
allowedEffects, especificando que las operaciones (copiar y / o en movimiento) se le permiten.

Con
DataObject se crea automáticamente un nuevo objeto.

2. Esto a su vez provoca el evento
GiveFeedback. En la mayoría de los casos  no necesitamos preocuparnos sobre el evento GiveFeedback, pero si deseamos mostrar un puntero de ratón personalizado durante el arrastre, aquí es donde debemos agregar el código.

3. Ningún tipo de control con la propiedad
AllowDrop establecido en True es un destino de colocación. La propiedad AllowDrop se puede configurar en la ventana Propiedades en tiempo de diseño o programación en el evento Form_Load.

4. Cuando el ratón pasa por encima de cada control, se activa el evento DragEnter para ese control. El método GetDataPresent se utiliza para asegurarse de que el formato de los datos es adecuado para el control de destino, y la propiedad Effect se utiliza para mostrar el puntero del mouse adecuado.

5. Si el usuario suelta el botón del ratón sobre un destino válido, se activa el evento DragDrop. Codificar en el controlador de eventos DragDrop el código que extrae los datos del objeto DataObject y lo muestra en el control de destino.

Para la mayoría de los casos más sencillos, se puede habilitar arrastrando escribiendo una sola línea de código, y puede definir la suelta con sólo unas pocas líneas más de código.

Arrastrando entre Listas

Otro escenario común donde  arrastrar y soltar  es una operación muy apreciada  consiste en mover elementos de una lista de elementos a otra. Normalmente, los botones también están disponibles en este escenario, pero se requieren dos clicks del ratón (seleccionar el elemento, y hacer click en el botón). Arrastrar y soltar es más eficiente aquí, ya que sólo requiere un solo movimiento (seleccionar y arrastrar). Para mover varios elementos de ida y vuelta entre dos listas

1. Agregar dos controles
ListView de un formulario.
2. Establecer la propiedad
AllowDrop de cada control ListView en true.
3. Establezcer la propiedad
MultiSelect de cada control ListView en true.
4. Establecer la propiedad
View de cada control ListView List.
5. Agregar el siguiente código:

En el Form_Load:

'Carga de valores en el ListView1
ListView1.Items.Add("Camas")
ListView1.Items.Add("Vendas")
ListView1.Items.Add("Jeringas")
ListView1.Items.Add("Goteros")

'Acciones al arrastar un objeto de la lista
Private Sub ListView_ItemDrag(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemDragEventArgs) Handles ListView1.ItemDrag, ListView2.ItemDrag

Dim miObjetoLista As ListViewItem
Dim miObjetosLista(sender.SelectedItems.Count - 1) As ListViewItem
Dim i As Integer = 0

'Bucle a través de la la colección de elementos seleccionados de la fuente.
      For Each miObjetoLista In sender.SelectedItems
            ' Añade al array los objetos elegidos del ListView.
            miObjetosLista(i) = miObjetoLista
            i = i + 1
      Next
'Crea un DataObject que contiene el array de objetos del ListView.
            sender.DoDragDrop(NewDataObject("System.Windows.Forms.ListViewItem()", miObjetosLista), DragDropEffects.Move)
End Sub

'Comprueba que los datos se puedan dejar en el listView de destino
Private Sub ListView_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListView1.DragEnter, ListView2.DragEnter

' Checkea el array de objetos para el formato de datos del cliente.
If e.Data.GetDataPresent("System.Windows.Forms.ListViewItem()") Then
      e.Effect = DragDropEffects.Move
Else
      e.Effect = DragDropEffects.None
End If

End Sub

'Acciones al soltar los elemntos arrastrados sobre el otro ListView
Private Sub ListView_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListView1.DragDrop, ListView2.DragDrop

Dim miObjetoLista As ListViewItem
Dim miObjetosLista() As ListViewItem = e.Data.GetData("System.Windows.Forms.ListViewItem()")
Dim i As Integer = 0

For Each miObjetoLista In miObjetosLista
      'Añade objetos a la lista del ListView de destino.
      sender.Items.Add(miObjetosLista(i).Text)
      ' Elimina los objetos  de la lista de origen.
      If sender Is ListView1 Then                                                 ListView2.Items.Remove(ListView2.SelectedItems.Item(0))
      Else                                                                        ListView1.Items.Remove(ListView1.SelectedItems.Item(0))                End If
      i = i + 1
Next

End Sub
       
Podríamos preguntarnos por qué este ejemplo utiliza los controles ListView en lugar de controles ListBox. Hay una buena razón: el control ListBox no soporta arrastrar varios elementos. Al hacer click en la lista, se  invalida la selección múltiple.

El ListView y TreeView tienen un evento ItemDrag que facilita el arrastrado.
En el ejemplo anterior, un solo evento  ItemDrag  cubre los controles, que se indican en la cláusula Handles. El parámetro sender representa que el control está  iniciando el arrastre.

Debido a que la clase DataFormats no incluye un miembro de tipo ListViewItem, los datos se deben pasar como un tipo del sistema. El código de ItemDrag crea una array de tipo ListViewItem, que se llena recorriedo los objetos de la colección SelectedItems.

En el método DoDragDrop, se crea un nuevo objeto DataObject  y se rellena con el array. Esta misma técnica se puede utilizar para arrastrar y soltar cualquier tipo de objeto del  sistema. En el evento DragDrop, el array se copia del  DataObject en un nuevo array de ListViewItem , y cada ListViewItem se agrega a la colección de elementos del control ListView destino.

No hay comentarios:

Publicar un comentario