Retomando el post anterior, vamos a ver -ahora sí- cómo funciona realmente el evento Draw del DrawingArea y cómo nos va a facilitar las cosas para crear movimiento, gráficos incluso algún juego interactivo.
Anteriormente vimos que la Propiedad Cached debía de establecerse a True para evitar el borrado de nuestros dibujos y que para dibujar debíamos de encerrar nuestras sentencias de dibujo entre dos métodos Draw.Begin(device) y Draw.End(). Pues bien, si ahora hacemos lo contrario, es decir, establecemos Cached = False y omitimos los métodos Begin y End… bueno alguno habrá comprobado que eso no funciona a menos que pongamos nuestras funciones de dibujo dentro del manejador de eventos _Draw() del DrawingArea (en adelante, «DA» para abreviar).
Lo vemos con un sencillo ejemplo que dibuja una pelota:
1 2 3 4 5 6 7 | Public Sub DrawingArea1_Draw() Draw.FillColor = Color.Red Draw.FillStyle = Fill.Solid Draw.Circle(DrawingArea1.Width / 2, DrawingArea1.Height / 2, 50) End |
Obtenemos lo que parece la bandera de Japón (WTF?), pero a pesar de que la propiedad Cached está en False, el dibujo no se borra hagamos lo que hagamos.
La razón es porque el DA se vuelve a redibujar siempre que sea necesario, por ejemplo cuando pasamos otra ventana por encima o redimensionamos el formulario. Si quieres comprobar cuándo se ejecuta el evento _Draw(), simplemente inserta una sentencia Print «lo que sea» dentro del evento y podrás verlo. No olvides comentar o borrar dicha sentencia después.
Hasta aquí hemos dejado claro cómo y cuándo funciona el evento _Draw() pero hay otra forma de dispararlo a voluntad, que es empleando el método .Refresh() del DA. Como el control DA es global a nivel de clase, podemos llamarlo desde cualquier parte del código cuando sea necesario. De manera que, para resumir un poco, todas las rutinas de dibujo las vamos a ejecutar desde dentro del manejador del evento _Draw() y llamaremos al método .Refresh cuando nosotros decidamos, o de una manera periódica como por ejemplo desde un Timer o desde otra función. El código dentro del evento _Draw se encarga sólo de dibujar, mientras que las rutinas que modifican lo que se dibuja se ejecutan desde fuera de dicho evento y modifican variables globales (a nivel de clase, Private).
Vamos a modificar el mismo ejemplo para que la «pelota roja» se mueva o cambie de color, simplemente cambiando los valores de las propiedades por variables y sin tocar estructuralmente el contenido del bloque de código dentro de Public Sub DrawingArea1_Draw().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ' Gambas class file Private posicionX As Integer Private posicionY As Integer Private iColor As Integer Public Sub DrawingArea1_Draw() Draw.FillColor = iColor Draw.FillStyle = Fill.Solid Draw.Circle(posicionX, posicionY, 20) End Public Sub btnCambiar_Click() Randomize posicionX = Rnd(0, DrawingArea1.Width) posicionY = Rnd(0, DrawingArea1.Height) iColor = Rnd(Color.Black, Color.White) DrawingArea1.Refresh End |
Todas las modificaciones las hacemos desde el manejador de eventos del botón «btnCambiar» que actualiza los valores de las variables y finalmente obligamos al DA a redibujar su contenido mediante .Refresh() que obliga a disparar el evento Draw y el código que contiene, tomando los nuevos valores de las variables. Como ejercicio adicional, podéis probar a hacer lo mismo usando un Timer con un intervalo de 1000 (1000 milisegundos = 1 segundo), en lugar de un botón.
A estas alturas creo que ha quedado claro el funcionamiento del evento Draw del DrawingArea y de cómo estructurar el código para separar la lógica por un lado, y de lo que estamos dibujando, por otro. Pensar en las posibilidades de animar dibujos empleando un Timer para refrescar automáticamente el lienzo donde estamos dibujando. Este será el objetivo de la siguiente entrada, donde veremos como representamos un punto en el sistema de coordenadas del drawing area, cómo mover ese punto y conceptos sobre vectores de posición y velocidad. Hasta entonces, sed buenos 😉
¿ Como no usaste randomize en Form_Open ?. (Para solo una vez)
Creo que hace lo mismo, que llamarlo varias veces desde el evento Click.
Lo mismo es totalmente indiferente.
También es posible hacerlo. Al ponerlo en el evento click lo que ocurre es que se renueva la semilla cada vez, pero no tiene mayor importancia para este ejemplo. No puse el evento Form_Open para simplificar el código.
Hey! buena explicación. Gracias.
Estaré esperando los próximos posts.