Es bastante frecuente que nos encontremos en situaciones donde hemos de ajustar el valor de los elementos de interfaz de usuario desde código. El problema en muchos de esto casos es que, al hacerlo, el control en cuestión lanza un evento como respuesta; el mismo cuyo código habremos implementado para reaccionar cuando es el usuario quien ajusta el valor. Seguramente, eso no es lo que deseemos. Por ejemplo, esto es lo que ocurre con los CheckBox, RadioButton y otra serie de elementos. ¿Cómo haremos entonces para ofrecer un comportamiento distinto si ajustamos el valor desde código o si es el usuario quien realiza una acción sobre el control? Es decir, seguramente no deseemos que estos controles ejecuten el mismo código implementado para ejecutarse cuando es el usuario quien hace “clic” o “tap” sobre ellos, cuando lo que necesitamos es simplemente que presenten un determinado valor por omisión en la ventana, página o vista. La respuesta: subclases.
Como seguramente ya sepas, recurrimos a la creación de subclases en la programación orientada a objetos cuando necesitamos obtener un comportamiento especializado a partir de la clase original. Podemos crear tantas subclases como deseemos a partir de una misma clase padre, lo que significa que una vez definidas podremos añadirlas a nuestra caja de herramientas y tenerlas listas para usar en todos los proyectos en los que se requiera ese mismo comportamiento. Por tanto, no tenemos por qué limitarnos a utilizar siempre solamente las clases proporcionadas originalmente en el framework de Xojo; sino que, con el paso del tiempo, podremos ir ampliándolas con aquellas que ofrezcan una respuesta a los diferentes problemas que vayamos encontrando en la implementación de nuestras soluciones. La otra opción sería la menos óptima: remendar y personalizar cada clase estándar cada vez que necesitásemos obtener una especialización o funcionalidad diferentes en cada uno de los proyectos realizados.
Para ver la respuesta al problema planteado en este caso concreto nos centraremos en el control CheckBox, si bien podrás aplicar muy probablemente los mismos principios sobre otros controles gráficos del framework en los que se plantee la misma problemática: responder de forma diferente cuando se lance el Evento (en este caso Action) como respuesta al cambio de estado desde código.
El primer paso consiste en añadir una nueva clase al proyecto (Desktop en este ejemplo), definiendo a continuación su propiedad Super a CheckBox y proporcionando como nombre MyCheckBox u otro más significativo a través del Panel Inspector. Con ello estaremos diciendo que la nueva clase MyCheckBox desciende de la clase CheckBox y, por tanto, heredará todas sus propiedades, eventos y métodos, que pasarán a estar también disponibles para nuestra nueva clase. A partir de aquí, vayamos con la especialización. (También puedes crear una clase personalizada arrastrando el contro propiamente dicho desde la Librería sobre el Navegador de Proyecto.)
Lo primero que añadiremos a nuestra nueva clase será una propiedad de tipo booleano
y cuyo ámbito será Privado. Esto significa que dicha propiedad sólo podrá ser accedida desde las propias instancias (objetos) creados a partir de la clase. Su cometido no será otro sino guardar el estado correspondiente a que el usuario ha hecho clic sobre el control, claro indicativo de que su valor no está siendo alterado desde código. Por ejemplo, podemos nomrbar dicha propiedad como interaccionUsuario
.
Lo siguiente que hará nuestra subclase será implementar el evento MouseDown ya que hemos de detectar si el usuario ha hecho clic dentro del control. Una vez añadido el evento (Add to “MyRadioButton” > Event Handler…), incluye el siguiente código:
interaccionUsuario = True return RaiseEvent MouseDown(X, Y)
Probablemente la segunda línea sea la menos obvia. Dado que nuestra definición de clase ya está consumiendo el Evento MouseDown, este no estará disponible para las instancias creadas a partir de la misma, algo que resolveremos añadiendo (definiendo) un nuevo Evento con la misma signatura y llamándolo mediante la palabra clave RaiseEvent. Pasaremos por tanto a la instancia los mismos argumentos que recibimos (posición del apuntador en las coordenadas “x” e “y”), y también devolveremos el booleano arrojado por la instancia. Por tanto, añade una nueva definición de Evento con Add to “MyRadioButton” > Event Definition y utiliza los siguientes datos en el panel Insepector asociado:
- Event Name: MouseDown
- Parameters: X as Integer, Y as Integer
- Return Type: Boolean
A continuación añadimos el evento Action, escribiendo el siguiente código:
If interaccionUsuario = True Then RaiseEvent Action interaccionUsuario = False End If
Nuevamente, dado que nuestra subclase consume el evento Action, tendremos que definir un nuevo evento con la misma signatura. De este modo, las instancias creadas a partir de la subclase podrán implementar su funcionalidad en respuesta a la Acción. La diferencia en este caso es que el evento Action de la subclase se ejecutará en primer lugar, y sólo pasará el control al código añadido en el evento Action de la instancia —mediante la instrucción RaiseEvent— si este se corresponde con un clic (o tap) realizado por el usuario; justo lo que queremos.
Con esto ya tenemos todo lo necesario para poner nuestra subclase a prueba. Añade a la ventana por omisión del proyecto un control PushButton y, por supuesto, una instancia de nuestra clase MyCheckBox. Para ello sólo tendrás que arrastrar el icono de la clase recién definida desde el Navegador de Proyecto sobre la ventana.
Una vez que nuestra interfaz de usuario está completa, ya podemos implementar el evento Action sobre el PushButton con el siguiente código:
MyCheckBox1.Value = Not MyCheckBox1.Value
Con ello cambiamos el estado de la instancia del CheckBox basado en nuestra clase sin que se ejecute el evento Action sobre este.
Añadimos también el evento Action sobre la instancia del CheckBox y añadimos el siguiente código:
MsgBox "The user clicked me!"
Es decir, sólo debería de mostrarse el mensaje cuando es el usuario quien hace “clic” sobre el control y no de otro modo.
Con esto ya habremos completado nuestra app de prueba para la clase, de modo que sólo quedará ejecutarla para ver su funcionamiento.
En definitiva, hemos visto como podemos añadir especialización mediante la creación de una subclase para uno de los controles proporcionados de serie en el framework de Xojo. Dicha especialización, además, se corresponde con una necesidad bastante común para cualquiera de nuestros proyectos.