Curso Básico de Programación
en Visual Basic

 

Entrega Cuarenta y cuatro: 07/Ene/2003
por Guillermo "guille" Som

Si quieres linkar con las otras entregas, desde el índice lo puedes hacer

 

Algunos de los que me escriben sobre este Curso Básico de Programación con Visual Basic, me suelen preguntar cuando se acabará el curso, creo que no se refieren a cuando me hartaré yo de escribir, sino a cuando pienso darlo por terminado. La verdad es que a esa pregunta no tengo respuesta, por la sencilla razón de que este curso lo escribo al "vuelo", es decir, no tengo un planteamiento previo del contenido, la verdad es que a algunos le puede extrañar, pero... es cierto, no tengo un planteamiento sobre el contenido, por eso en algunas entregas te digo que en la siguiente hablaré de un tema y al final acabo hablando de otro, en algunas ocasiones, totalmente distinto al previsto.

En esta entrega voy a seguir con el tema que propuse en la entrega anterior, es decir, vamos a seguir hablando del polimorfismo y las interfaces, pero también trataremos otro tema que, casi con seguridad te será de utilidad en algunas aplicaciones: cómo hacer una ventana de configuración que sea algo "inteligente", es decir, que tengamos un botón Aplicar y si modificamos el contenido de las opciones presentadas, ese botón se habilite, pero si después de hacer cambios, volvemos a dejar todo como estaba originalmente, ese botón se deshabilite.
Para conseguirlo, usaremos una clase en la que estarán los valores de cada una de las opciones que la ventana de configuración va a manejar, de forma que se use esa clase para comunicarse con el formulario que llame a la ventana de configuración. También crearemos una clase/interfaz, que nos sirva para implementarla tanto en el formulario de configuración como en la clase usada para mantener esos datos.
Seguramente, cuando veas el código, pensarás que no era necesario usar clases ni interfaces, y estarás en lo cierto, pero... como el tema que estamos tratando en estas últimas entregas, es precisamente las clases, el polimorfismo y las interfaces... pues eso...

Así que, sin más preámbulos, empecemos, (o mejor dicho), sigamos con esta entrega.

Antes de empezar a mostrarte el código, voy a explicarte un poco el planteamiento que vamos a hacer, para que te resulte más fácil seguir el código que te mostraré.

Tendremos una clase con una serie de propiedades, que en este caso simplemente son para poder hacer las pruebas y el ejemplo, seguramente no será de mucha utilidad, pero con ella veremos los conceptos, para que puedas aplicar los tuyos propios, ya que, lo que me gustaría es que "siempre" te quedes con la base de lo que te explico, no que uses el código directamente, ya que con ello no llegarás a aprender, y de lo que aquí se trata es que aprendas a hacer las cosas para que después lo apliques como más te convenga.

Como te comentaba en la entrega anterior, en la versión de Visual Basic de la que trata este curso, (la 6.0 y anteriores, realmente hasta la 5, ya que las anteriores no aceptan la implementación y otras de esas monerías de la OOP), se puede usar cualquier clase como una interfaz a implementar. Esa clase puede contener código, aunque cuando se implementa ese código "desaparece" y sólo se queda "la firma" o estructura de la clase, es decir los métodos y propiedades públicas que contenga. También te recomendé que en lugar de usar una clase para realizar la implementación, crearas una clase en la que sólo estén definidos los miembros de la interfaz, por tanto aquí vamos a seguir las recomendaciones que te di en la entrega anterior, para que no digas que es muy fácil recomendar como hacer las cosas y después hacerla de una forma diferente.

Una cosa que debes saber, no se si ya te lo he dicho, es que cuando declaramos una variable pública en una clase, esa variable se convierte automáticamente en una propiedad. Por tanto, si sólo declaramos las variables, en lugar de crear propiedades propiamente dichas, cuando implementemos esa clase, se crearán los correspondientes procedimientos Property Let y Property Get. Te digo esto para que no te extrañes cuando te ocurra.

De todas formas, en los ejemplos que voy a utilizar en esta entrega, definiré los miembros de la interfaz de la forma "correcta", es decir usando Property Let/Get en lugar de declarar las variables, realmente sólo se declararán las propiedades de lectura (Get), ya que no es necesario declarar las de escritura (Let), entre otras cosas, porque no se van a usar.


La aplicación que vamos a usar de ejemplo, tendrá un formulario principal y otro que será el que se utilice para configurar ciertos valores, además tendremos dos clases, una de ellas será la interfaz y la otra, la clase propiamente dicha, en la que se almacenarán los datos que usaremos.
Seguramente, cuando veas el código y el diseño de los formularios, te parecerán iguales o muy parecidos, esto es así, para simplificar, ya que no es plan de hacer una aplicación demasiada "completa" para ver el "corazón" de lo que aquí te quiero explicar. Por supuesto, tendrás que hacer algo por tu propia cuenta si quieres que el ejemplo sea más "sofisticado", ya que, como te he comentado al principio, lo que me interesa enseñarte es cómo hacerlo, no darte el código completo para que lo copies y pegues... es que si no pones de tu parte, no llegarás a aprender... ya que considero que con todo lo que hasta ahora he explicado en este curso básico, deberías tener la "base" suficiente para que no te resulte difícil investigar por tu cuenta y riesgo, de forma que amplíes los conocimientos que, se supone, hasta ahora has ido acumulando.

Veamos el aspecto del formulario principal, así como el de configuración.
Como te he comentado, el formulario principal tendrá una serie de controles, los cuales se usarán para almacenar unos datos y esos datos se podrán modificar con el de configuración, para que el funcionamiento "aparente" de los dos formularios no sea el mismo, el formulario principal leerá y almacenará los datos en un fichero de texto. Pero quiero que pongas en funcionamiento tu imaginación e imagines que el formulario principal hace más cosas... así no te parecerá que los dos formularios hacen lo mismo.

De todas formas, la parte de Leer y Guardar te las dejo a ti como ejercicio, así comprobarás si has aprendido algo...

Este es el aspecto de los dos formularios que usaremos en este ejemplo:


Fig. 1, el formulario principal

 


Fig. 2, el formulario de configuración

 

En el formulario de configuración, el botón Aplicar se habilitará o deshabilitará según se hayan o no modificado los datos, cuando se pulse en dicho botón, se asignarán los nuevos valores a una variable basada en una clase.
Cuando se pulse en Aceptar, también se asignarán esos valores y se ocultará el formulario para que el formulario principal tome nuevamente el control.
Cuando se pulse en Cancelar, se descartarán los cambios y se ocultará el formulario para poder volver al formulario principal.

Nota (o truco):
Si a la propiedad StatUpPosition del formulario de configuración le asignas el valor 1 - CenterOwner, hará que se muestre centrado con respecto a la posición del formulario principal.

 

Asignar los botones predeterminados de un formulario para Cancelar y Aceptar

Cuando se hace un formulario al estilo del mostrado para la configuración, el botón Cancelar se suele asociar con la tecla ESC y el de Aceptar con la tecla Intro, de forma que cuando se pulse cualquiera de esas dos teclas se ejecuten las acciones indicadas en esos botones.

Esta funcionalidad se consigue asignando unas propiedades que todos los botones (CommandButton) tienen:
Para asignar un botón como predeterminado (se ejecuta el evento Click al pulsar Intro), hay que asignar un valor True a la propiedad Default.
Por otro lado, para que un botón actúe como receptor de la tecla Escape, hay que asignar un valor True a la propiedad Cancel.

Después veremos el código de este formulario, aunque antes hay que saber cómo hacer que se muestre dicho formulario... y esperar a que se elijan las opciones.

 

Cómo llamar a un formulario desde otro

Aunque esto ya lo hemos visto, (creo), vamos a verlo nuevamente, aunque sea de pasada.

Para que un segundo formulario se muestre, podemos hacerlo de varias formas, aunque la forma en que dicho formulario se comportará sólo puede ser de dos formas distintas:

1- Que se muestre, pero que el formulario que lo ha llamado (o mostrado) siga ejecutándose, es decir no se espera a que el segundo formulario termine. Esto puede ser útil cuando necesitamos que ese segundo formulario actúe de forma independiente del primero, pero ese no es el comportamiento esperado cuando se muestra un formulario, como en nuestro caso, que se vaya a usar como configuración.

2- Que se muestre, pero que hasta que no se cierre u oculte, el formulario que lo ha llamado no continúe. Este caso es el que nos interesa, además de que es el que normalmente utilizan los formularios de configuración, también llamados "cuadros de diálogo".

Para el primer caso se usaría lo siguiente:
NombreFormulario.Show
o también de esta forma:
NombreFormulario.Show vbModeless
Es decir, muéstralo pero en forma no-modal (o no como cuadro de diálogo).

Para el segundo caso, habrá que indicarle al método Show que se quiere mostrar en modal, es decir como un cuadro de diálogo, para ello habrá que usar:
NombreFormulario.Show vbModal

Los valores de vbModal y vbModeless realmente son constantes de Visual Basic cuyos valores son uno y cero respectivamente.

Como te he comentado, la principal diferencia entre estas dos formas de mostrar un formulario, es que en modo no-modal, se mostraría el formulario, pero se continuaría en la siguiente línea, por ejemplo:

Form2.Show

MsgBox "Después de mostrar el formulario"

En este caso, se mostraría el formulario indicado (Form2) y justo después de mostrarse, se ejecutaría la instrucción MsgBox y el formulario seguiría abierto.

Pero si lo mostramos de forma modal:

Form2.Show vbModal

MsgBox "Después de mostrar el formulario"

La instrucción MsgBox no se ejecutaría hasta que no se cerrara u ocultara el Form2. Es decir, esperaría a que se cierre el formulario antes de continuar con la siguiente instrucción.

En el ejemplo que veremos aquí se usa esta segunda forma.

 

Una vez hechas estas aclaraciones, veamos el código, empezando por la interfaz IConfig, la cual se implementará tanto en la clase "real" cConfig, como en el formulario de configuración, al cual llamaremos fConfig.

 

La clase (interfaz) IConfig:

'------------------------------------------------------------------------------
' IConfig                                                           (06/Ene/03)
' Interfaz para usar con un formulario de configuración
'
' ©Guillermo 'guille' Som, 2003
'------------------------------------------------------------------------------
Option Explicit


Public Property Get Valor1() As String

End Property

Public Property Get Valor2() As String

End Property

Public Property Get Valor3() As String

End Property

Public Property Get Opcion1() As Boolean

End Property


Como puedes comprobar, sólo se definen los procedimientos de lectura (Property Get), pero no se utiliza ningún código "operativo", ya que esta clase sólo se usa como "plantilla".

 

La clase cConfig:

'------------------------------------------------------------------------------
' cConfig                                                           (06/Ene/03)
' Clase para usar con un formulario de configuración
'
' ©Guillermo 'guille' Som, 2003
'------------------------------------------------------------------------------
Option Explicit

Implements IConfig

Public Valor1 As String
Public Valor2 As String
Public Valor3 As String
Public Opcion1 As Boolean

Private Property Get IConfig_Opcion1() As Boolean
    IConfig_Opcion1 = Opcion1
End Property

Private Property Get IConfig_Valor1() As String
    IConfig_Valor1 = Valor1
End Property

Private Property Get IConfig_Valor2() As String
    IConfig_Valor2 = Valor2
End Property

Private Property Get IConfig_Valor3() As String
    IConfig_Valor3 = Valor3
End Property

Public Sub CopiarIConfig(ByVal newValue As IConfig)
    ' hacer una copia del parámetro en los valores de la clase
    Me.Opcion1 = newValue.Opcion1
    Me.Valor1 = newValue.Valor1
    Me.Valor2 = newValue.Valor2
    Me.Valor3 = newValue.Valor3
End Sub

Public Function Clone() As cConfig
    Dim tConfig As cConfig
    Set tConfig = New cConfig
    '
    tConfig.CopiarIConfig Me
    '
    Set Clone = tConfig
End Function

Public Function Equals(ByVal compararCon As cConfig) As Boolean
    Dim b As Boolean
    '
    If compararCon.Opcion1 = Me.Opcion1 Then
        If compararCon.Valor1 = Me.Valor1 Then
            If compararCon.Valor2 = Me.Valor2 Then
                If compararCon.Valor3 = Me.Valor3 Then
                    b = True
                End If
            End If
        End If
    End If
    '
    Equals = b
End Function

Public Sub Guardar(ByVal fichero As String)
    ' guardar en el fichero indicado los valores de las propiedades de la clase
    '
    ' el código está omitido para que lo hagas como ejercicio
    '
End Sub

Public Sub Leer(ByVal fichero As String)
    ' leer del fichero indicado los valores de las propiedades de la clase
    '
    ' el código está omitido para que lo hagas como ejercicio
    '
End Sub


En esta clase, hemos implementado IConfig, para que al acceder a esa "parte" del código, se obtengan los datos de las propiedades.
También se han implementado algunos métodos, los cuales servirán para Leer y Guardar información en un fichero de disco, (ese código es tarea tuya), por otro lado, tenemos un método que nos permitirá efectuar una copia del objeto: Clone, otro que comprobará si el contenido de esta clase es idéntico al de otra del mismo tipo: Equals y por último, un método, (CopiarIConfig), que recibe como parámetro un objeto del tipo IConfig, que se usará para asignar a las propiedades de la clase los valores indicados en el parámetro, este método se usará para "actualizar" el contenido de las propiedades de la clase con otro objeto externo del tipo IConfig o que implemente la interfaz IConfig.
Fíjate que el método Clone, hace uso de ese método para realizar la copia de los datos del objeto actual en la nueva variable que devuelve.

 

El código del formulario principal:

'------------------------------------------------------------------------------
' fEntrega44                                                        (06/Ene/03)
' Pruebas para la entrega 44 del Curso Básico de VB
'
' ©Guillermo 'guille' Som, 2003
'------------------------------------------------------------------------------
Option Explicit

Private mConfig As cConfig
Private sFic As String

Private Sub Form_Load()
    ' el nombre del fichero de datos
    sFic = App.Path & "\Prueba44.txt"
    '
    Set mConfig = New cConfig
    ' asignar los valores de prueba al objeto
    mConfig.Valor1 = "El valor1"
    mConfig.Valor2 = "El valor2"
    mConfig.Valor3 = "El valor3"
    mConfig.Opcion1 = True
    ' mostrarlos en los controles del formulario
    asignarConfig mConfig
    '
End Sub

Private Sub cmdCerrar_Click()
    Unload Me
End Sub

Private Sub cmdGuardar_Click()
    ' guardar los datos en un fichero
    mConfig.Guardar sFic
End Sub

Private Sub cmdLeer_Click()
    ' leer los datos del fichero indicado
    mConfig.Leer sFic
    '
    '
    '$POR HACER: actualizar los controles con los valores de mConfig
    '
    '
End Sub

Private Sub cmdConfig_Click()
    ' no es necesario crear una variable para acceder al formulario
    ' pero es una buena práctica, la cual te recomiendo encarecidamente.
    Dim fc As fConfig
    '
    Set fc = New fConfig
    ' cargamos el formulario para que se inicialicen los valores que pudiera
    ' haber en el evento Form_Load
    Load fc
    '
    ' llenar el combo del formulario de configuración
    ' con algunos datos
    Dim i As Long
    '
    For i = 1 To 10
        fc.Combo1.AddItem "Valor de prueba número " & CStr(i)
    Next
    '
    ' Asignamos los valores a la clase
    mConfig.Valor1 = Text1
    mConfig.Valor2 = Text2
    mConfig.Valor3 = Text3
    If Check1.Value = vbChecked Then
        mConfig.Opcion1 = True
    Else
        mConfig.Opcion1 = False
    End If
    '
    ' usando un procedimiento
    fc.AsignarIConfig mConfig
    '
    ' mostramos el formulario de forma modal para que "espere"
    ' hasta que se haya cerrado u ocultado
    fc.Show vbModal
    '
    ' comprobamos si se ha cancelado la configuración
    If fc.Cancelado = False Then
        ' sólo asignar los valores si no se ha cancelado
        '
        ' asignamos los nuevos valores a la copia local de este formulario
        ' si se pasa como parámetro del tipo IConfig, funcionará usando el formulario
        asignarConfig fc
        '
    End If
    '
    ' descargamos el formulario de configuración y
    Unload fc
    ' eliminamos el objeto de la memoria
    Set fc = Nothing
End Sub

Private Sub asignarConfig(ByVal newValue As IConfig)
    ' actualizamos el contenido de la clase
    mConfig.CopiarIConfig newValue
    '
    ' actualizamos el contenido de los controles
    Text1 = newValue.Valor1
    Text2 = newValue.Valor2
    Text3 = newValue.Valor3
    If newValue.Opcion1 Then
        Check1.Value = vbChecked
    Else
        Check1.Value = vbUnchecked
    End If
End Sub


Veamos con detalle el código usado:

Declaramos una variable del tipo cConfig para almacenar los datos (mConfig).
Declaramos una variable que tendrá el nombre del fichero en el que se guardarán los datos (sFic).
En el evento Form_Load, asignamos el nombre del fichero, el cual estará en el mismo directorio que la aplicación, para ello utilizamos App.Path, a continuación creamos una nueva instancia del objeto mConfig, le asignamos unos valores "de prueba" y llamamos al método asignarConfig para que se reflejen en los controles los valores de la clase.

Cuando se pulsa en el botón Cerrar, se descarga el formulario principal, con lo cual también se termina la aplicación: Unload Me.
Al pulsar en el botón Guardar, se llama al método Guardar de la clase cConfig y se le pasa como parámetro el nombre del fichero en el que se guardará el contenido de esa clase. Fíjate que será el propio método Guardar de la clase el que se encargue de abrir ese fichero y guardar el contenido de las propiedades.
Cuando se pulse en el botón Leer, se llama al método Leer del objeto mConfig, (que es del tipo cConfig), el cual se encargará de leer la información del fichero pasado como argumento (o parámetro), ese método (el de la clase) será el que se encargue de, además de leer la información del fichero, comprobar si ese fichero existe, etc. Una vez leídos los datos, se tendrá que reflejar esa información en los controles del formulario, esto también es algo que debes hacer como ejercicio, sólo comentarte que la solución es muy simple y ya está hecha... o casi.
Cuando el usuario pulse en el botón Configurar, tendremos que mostrar el formulario de configuración, al cual tenemos que indicar los valores originales a mostrar y una vez cerrado, de forma "aceptable", esos datos hay que asignarlos a la clase (en este caso) del formulario principal.

Nota:
Aquí estamos usando una clase para mantener la información en el formulario principal, en otras ocasiones, es posible que esa información esté asignada a valores de variables "normales", pero el planteamiento que te estoy explicando es para usar clases e interfaces, por tanto, he elegido ese método o forma de comunicación entre los formularios y los datos a configurar.

Vamos a hacer una pequeña "parada" o repaso más detallado sobre el código usado en el evento del botón Configurar, ya que aquí hay un par de cosillas, creo que, "interesantes".
La primera de ellas es que hemos declarado una variable para usar el formulario de configuración. Realmente no es necesario ni obligatorio hacerlo de esta forma, aunque te recomendaría que te acostumbraras a hacerlo así.
La forma más sencilla, hubiera sido usar directamente el nombre que le hemos asignado al formulario: fConfig, ya que el Visual Basic declara y crea una variable "global" con dicho nombre. Por tanto, si eliminas o comentas las líneas que hay antes del Load fc y cambias todos los fc por fConfig, el programa seguirá funcionando igual.
Al crear una variable (fc) del tipo del formulario, lo que estamos haciendo, entre otras cosas, es comprobar que los formularios realmente son y actúan como clases. Y se "instancian" o crean de la misma forma que cualquier variable del tipo de una clase, es decir usando Set variable = clase.

Seguramente te preguntarás ¿por qué esa complicación extra?
Pues, porque sí... porque así puedes comprobar que puedes usar un formulario igual que cualquier otra clase y también porque así es más evidente cual es nuestra intención, si ese código lo ve cualquier otra persona, sabrá exactamente cual es tu intención.
Recuerda que quiero enseñarte "buenos modales" en esto de la programación, por tanto, siempre tendrás la libertad de hacerlo de la forma que más te guste: como el Guille te recomienda o "a las bravas", tu eres quién tiene la última palabra.

Después de esta pequeña aclaración sobre modales, sigamos examinando el código:
Después de cargar el formulario en la memoria (Load fc), asignamos una serie de valores al ComboBox, esto lo hago así, para que sepas que puedes acceder a cualquier control de un formulario desde otro y asignar o recuperar cualquiera de los valores que dicho control tenga.
El siguiente paso es asignar a las propiedades de la variable mConfig el contenido de los controles del formulario principal. Fíjate que en el formulario principal estamos usando una caja de textos para el contenido de la propiedad Valor3 y en el de configuración, (como comprobarás dentro de poco), se está usando un ComboBox.
Una vez que la variable mConfig tiene asignado los valores de las propiedades, la pasamos con parámetro del método AsignarIConfig que hemos implementado en el formulario de configuración, esto hará, (como verás dentro de poco), que se asignen los valores tanto a la variable del tipo cConfig que hay en ese formulario, como a los controles.
Hecho esto, el formulario de configuración tendrá toda la información que necesita, por tanto lo mostramos de forma modal (como cuadro de diálogo), usando fc.Show vbModal, de esta forma, el código del formulario principal "esperará" a que el formulario de configuración desaparezca de la faz de la pantalla, para continuar.
Cuando vuelve, comprobamos si se ha cancelado, en caso de que no se haya cancelado, la propiedad Cancelado tendrá un valor False y por tanto tendremos que aceptar los nuevos valores.
Esa propiedad (Cancelado) es una propiedad que nosotros (en este caso yo) hemos definido en el formulario de configuración, tal como podrás comprobar cuando veamos el código que falta.
Si no se ha cancelado, asignaremos a la variable mConfig del formulario principal los valores que se han asignado en el de configuración, para ello utilizamos el método asignarConfig, al cual se le pasa como parámetro el formulario (fc), pero ese método acepta un objeto del tipo IConfig, por tanto Visual Basic filtrará el formulario para que sólo se "vea" la parte implementada por la interfaz IConfig.
Por último se descarga el formulario (para que no siga en memoria) y se librera la memoria utilizada por la variable fc, aunque esto último no es necesario, ya que será el propio VB el que se encargue de hacer esa limpieza, pero esto es algo que yo acostumbro a hacer... ¡cosas mías!

Para acabar con la explicación del código del formulario principal, sólo nos queda ver el procedimiento asignarConfig.
Este procedimiento (o método privado) del formulario, recibe como parámetro un objeto del tipo IConfig, por tanto cualquier objeto de ese tipo o que lo implemente, se puede usar como parámetro.
Lo que se hace en ese procedimiento, es hacer una copia en la variable mConfig, (por medio del método CopiarIConfig), de los datos contenidos en el objeto pasado como parámetro, así nos aseguramos de que la variable siempre esté actualizada. También se asignan esos valores a los controles para que sea "visual" para el usuario.

Si algo de lo que acabamos de ver no te ha quedado muy claro, espera a ver el resto del código.

 

El código del formulario de configuración:

'------------------------------------------------------------------------------
' fConfig                                                           (06/Ene/03)
' Pruebas para la entrega 44 del Curso Básico de VB
'
' ©Guillermo 'guille' Som, 2003
'------------------------------------------------------------------------------
Option Explicit

Implements IConfig

Private mConfig As cConfig
Public Cancelado As Boolean

Private Sub Check1_Click()
    comprobarCambiado
End Sub

Private Sub cmdAceptar_Click()
    cmdAplicar_Click
    Cancelado = False
    Hide
End Sub

Private Sub cmdAplicar_Click()
    ' asignar los valores a la clase
    mConfig.Valor1 = Text1
    mConfig.Valor2 = Text2
    mConfig.Valor3 = Combo1.Text
    If Check1.Value = vbChecked Then
        mConfig.Opcion1 = True
    Else
        mConfig.Opcion1 = False
    End If
    '
    cmdAplicar.Enabled = False
End Sub

Private Sub cmdCancelar_Click()
    Cancelado = True
    Hide
End Sub

Private Sub Combo1_Change()
    comprobarCambiado
End Sub

Private Sub Form_Load()
    Set mConfig = New cConfig
    cmdAplicar.Enabled = False
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    Cancelado = True
End Sub

Private Sub Form_Resize()
    If WindowState <> vbMinimized Then
        Line1.X1 = 90
        Line1.X2 = ScaleWidth - 90
        Line2.X1 = 90
        Line2.X2 = ScaleWidth - 90
    End If
End Sub

Private Property Get IConfig_Opcion1() As Boolean
    IConfig_Opcion1 = (Check1.Value = vbChecked)
End Property

Private Property Get IConfig_Valor1() As String
    IConfig_Valor1 = Text1
End Property

Private Property Get IConfig_Valor2() As String
    IConfig_Valor2 = Text2
End Property

Private Property Get IConfig_Valor3() As String
    IConfig_Valor3 = Combo1.Text
End Property

Private Sub Text1_Change()
    comprobarCambiado
End Sub

Private Sub Text2_Change()
    comprobarCambiado
End Sub

Private Sub comprobarCambiado()
    Dim datosCambiados As Boolean
    '
    If mConfig.Valor1 <> Text1 Then
        datosCambiados = True
    End If
    If mConfig.Valor2 <> Text2 Then
        datosCambiados = True
    End If
    If mConfig.Valor3 <> Combo1.Text Then
        datosCambiados = True
    End If
    If mConfig.Opcion1 <> (Check1.Value = vbChecked) Then
        datosCambiados = True
    End If
    '
    cmdAplicar.Enabled = datosCambiados
End Sub

Public Sub AsignarIConfig(ByVal newValue As IConfig)
    ' esta asignación sólo funcionará si newValue es del tipo cConfig
    'Set mConfig = newValue
    mConfig.CopiarIConfig newValue
    '
    Text1 = mConfig.Valor1
    Text2 = mConfig.Valor2
    Combo1.Text = mConfig.Valor3
    If mConfig.Opcion1 Then
        Check1.Value = vbChecked
    Else
        Check1.Value = vbUnchecked
    End If
    comprobarCambiado
End Sub

En el formulario de configuración tenemos una variable (mConfig) del tipo cConfig, que será la que reciba los valores a "configurar"... (¡cuantas configuraciones!), también implementa la interfaz IConfig, además de tener una propiedad llamada Cancelado, (recuerda que las variables de una clase declaradas como públicas se convierten en propiedades), que nos servirá de "señal" para saber si el usuario ha cancelado o no la "configuración"...
Fíjate que en este formulario el nombre de la variable con los datos, también se llama mConfig, pero esto es sólo "pura coincidencia" (o mejor dicho, para no tener que calentarme la cabeza pensando en otro nombre), ya que no tiene porqué llamarse igual que la variable usada en el formulario principal. Además deberías saber que esa variable no se podrá "conectar" ni confundir con la del otro formulario, por la sencilla razón de que ambas están declaradas como "privadas", (Private), por tanto sólo visibles dentro de cada formulario.

En el evento Click de los botones Aceptar y Cancelar, se asigna el valor apropiado a la propiedad Cancelado, además de que se oculta (mediante Hide) el formulario, de esta forma se devuelve el control al formulario principal y se conservan los datos, ya que al no cerrarlo, lo seguimos teniendo en memoria.
En el caso del botón Aceptar, llamamos al procedimiento cmdAplicar_Click, para que se asignen los valores a la variable mConfig, esto es para simular el efecto de pulsar en el botón Aplicar, ya que ese botón se encarga de actualizar los datos de la variable con el contenido de los controles.

En cuanto al código de los eventos que se disparan cuando se cambia alguno de los controles con los datos, simplemente llaman al procedimiento comprobarCambiado en el cual se hacen las comprobaciones pertinentes para saber si se debe o no habilitar el botón Aplicar. De esta forma, si el usuario escribe o cambia cualquiera de esos valores, dicho botón estará habilitado (el contenido ha cambiado) o deshabilitado, para reflejar que el contenido de esos controles se ha dejado como estaba, realmente se comprueba si estaba como antes de haber pulsado en Aplicar.
Creo que el código del procedimiento comprobarCambiado no necesita ninguna explicación, (al menos detallada), ya que lo único que se hace es comprobar si el contenido de las propiedades de la clase coinciden con el contenido de los controles.
Si acaso, explicarte cómo funciona esta línea:
If mConfig.Opcion1 <> (Check1.Value = vbChecked) Then
Aquí lo que se comprueba es si el valor de Opcion1 (que es de tipo Boolean) es distinto del resultado de comprobar si el valor de Check1 es igual a vbChecked. Esa expresión devolverá True o False dependiendo de que el valor sea o no vbChecked, es decir que esté o no "marcado". Esto último lo tienes un poco más claro en el código del evento Click del botón Aplicar, ya que allí se hace una comprobación más "específica" y detallada.

El método público AsignarIConfig es el que se encarga de asignar los valores a la variable interna del tipo cConfig y a los controles con los valores que se hayan indicado, este método se llama desde el formulario principal para asignar los valores iniciales de la configuración.

Para terminar, en el evento Load del formulario se crea una nueva instancia de la variable "privada" que contendrá los datos de configuración, además de deshabilitar el botón Aplicar.
El evento QueryUnload se produce justo antes de que el formulario se cierre, por tanto este evento se producirá si el usuario pulsa en la "x" para cerrarlo, si es así, asignamos un valor False a la propiedad Cancelado, aunque, realmente no es necesario hacerlo, pero... para que quede evidente que si se cierra, se supone que se ha cancelado.
En el caso del evento Resize, se comprueba si no se ha minimizado y si es así, se posicionan las líneas que hacen de separador 3D, (las líneas que están encima de los tres botones).

 

Espero que todo haya quedado claro y comprensible, no sólo por los comentarios que he hecho al código, sino por los propios comentarios que el código contiene.
En caso de que no te hayas enterado... pues léete de nuevo esta entrega y sobre todo la anterior, para ver si así te aclaras un poco... (es que si te digo que me preguntes, no te vas a esforzar en entenderlo y me dará la impresión de que "realmente" no quieres esforzarte, así que... ¡no me preguntes!).

Recuerda que tienes que hacer los ejercicios que te he mencionado, la solución la veremos en la siguiente entrega, la cual aún no se de que tratará... ¿de clases? ¿de bases de datos? ¿se acabará ya el curso?
Pues, tendrás que esperar, como mínimo al mes que viene o a finales de este, según me de... je, je.

Mientras tanto, busca algo que leer o repásate las 43 entregas anteriores... que no es plan de que se te olviden las cosas por mi tardanza en escribir nuevas entregas.

Nos vemos
Guillermo
 

Aquí tienes el fichero zip con el código usado en esta entrega:  basico44_cod.zip 5.50 KB


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille