Using events as a way to pass information to instances

Most of the time when we talk about events the first thing that pops into someones head isa UI control and the events is has for interaction with the user. A pushbuttons Action (or Pressed) event for instance. Or the various events on listboxes for selecting rows, handling mouse clicks, keyboard events and the myriad of other user interface and interaction related things listboxes handle.

But thats not the only way to use events 🙂

Sometimes you may write a control and it needs to get data from the layout its on or from something else outside its control. So how do you get access to this ?

You could, as I’ve seen in some code, make everything PUBLIC and then the specific control just gets its parent control and grabs what ever property or calls whatever parent method it needs.

This is, IMHO, a bad practice as it makes your code more fragile since a change in the method or property you’re accessing this way could cause unrelated code to break. And because it destroys any encapsulation you might have created. Its a huge violation of the Law Of Demeter I wrote about before. You should only use things that are passed to you as parameters, that you own and control, or that are declared in your code. Reaching way outside to the parent control would definitely violate this principle.

So what can you do ?

As discussed before, subclasses CAN create NEW events and you could use an event to ask the containing layout for whatever property value or reference to something else the layout has access to.

We’ll create a small project to demonstrate the idea. What we will have is a custom canvas control that will ask the outside world for the color that it should draw itself as. You might normally do this as a simple property but I am NOT going do that to demonstrate. The other thing we will have on the layout is yet another custom canvas that acts simply as a color picker.

I’m using 2019r1.1 so some things may be different if your using a newer version

First start a new desktop project

Add a new subclass of Canvas by dragging the canvas control from the Library over to the Navigator (or you can add a new class and set its super to Canvas in the inspector). Name this new class ColorSelectorSwatch.

Add a public property called FillColor as Color

We’ll add the MouseUp event handler and put the following code in it

  Dim c As Color
  
  If SelectColor(c, "Select a color") Then
    Me.FillColor = c
    Me.Invalidate
    RaiseEvent Updated
  End If

Also add the Mouse Down event handler and put this code in it

  Return True

Also add a new Event Definition and name it Updated

Thats it for our little color selector swatch

Add an instance of our ColorselectorSwatch to the default layout, Window1, by dragging it from the Navigator on to the layout Window1. By default its name will be ColorSelectorSwatch1

Add the event handler for the new Updated event. We’re not going to put any code in it just yet. We’ll return to this later once we create our other custom control.

Now add another canvas subclass to your project. Again you can do this by dragging or by simply adding a class and changing the superclass in the inspector. Name this class CustomCanvas. This is the one we’re going to add the event to that will ask the outside world what color it should draw itself in.

Add the Event handler for the Paint event to the CustomCanvas.

Also add a new event definition colorToDrawWith() As color.

Note that this event handler has a return value that is a color. When we need to get the color we’ll raise this event, get whatever return value, and then paint our canvas with this color. *

The code for the Paint event is fairly simple

Dim c As Color

c = RaiseEvent ColorToDrawWith // call the event to get the color 
                               // from the outside world

g.ForeColor = c
g.FillRect 0,0, g.width, g.height

Drag an instance of this custom canvas onto the default window and make sure it does not overlap your other canvas. By default it will be named CustomCanvas1.

Now we’ll add the handler for the ColorToDrawWith event. All we need is one line

return ColorSelectorSwatch1.FillColor

Now return to the Updated event of ColorSelectorSwatch1. In there we need one line to make it so our CustomCanvas will know to redraw itself when the color selector swatch gets a new color selected. In the Updated event put

CustomCanvas1.Invalidate

And run. Every time you select a new color in the color swatch the other canvas will update and redraw itself. And, to do so we have used an event to return information to the control instance that tells it what color to draw with.

Here’s my completed sample

*Of note one of the things that got added in versions after 2019r1.1 is a way to tell IF the event is implemented. You could use this to know whether you should redraw with the color returned or not.

The only change would be to make the Paint event of CustomCanvas read as follows

If IsEventImplemented("ColorToDrawWith") Then
  Dim c As Color 
  
  c = RaiseEvent colorToDrawWith
  
  g.ForeColor = c
  g.FillRect 0,0, g.width, g.height
End If

2 Replies to “Using events as a way to pass information to instances”

  1. Norman,

    I have a question but before I do that,
    let me take this opportunity to thank you for sharing you’re insights and knowledge on Xojo. It is highly appreciated.

    Now the question, pure for my understanding the code, in the ColorSelectorSwatch.MouseUp event so have the following line:

    Me.Invalidate

    Why did you use it there, if I comment the line,
    the program still does the intended thing.

    The canvas: ColorSelectorSwatch has nothing to redraw after it is clicked.

    Thank you.

    1. An invalidate doesnt say “redraw this now” – it simply marks the area or control to be redrawn when the next paint event happens.
      If there is something to redraw then this will be redrawn. And if not then nothing happens.

      So sometimes an invalidate is simply “insurance” to make sure the item gets redrawn in the next paint event

Comments are closed.