Control Sets and ContainerControl: A New Approach

Control Setis thefeature to use when there are several instances in a Window’s layout and you need to command (or access them) from code, both for those available by default in the Framework and ones based on your own graphic classes. For example, this allows us to invoke a method in a concrete instance, based on its control index, or invoke the same method to all of them (iteration). All of this without knowing in advance, at runtime, how many of such instances are placed in the layout.

An inconvenience of this feature is that you can’t use it when the graphic controls are placed on a ContainerControl due to the way ContainerControls are implemented. The good news is that this problem has an easy solution! Read on to learn about it:

Creating Subclasses

First, create subclasses from those Framework controls you want to use as a Control Set; this also applies when creating your own graphic controls from scratch.

For that, add a new Classto a Xojo project from the Insertmenu and use the Inspector to set the class name and the Parent class it is derived from. For this example, use the following values:

  • Name: MMButton
  • Super: PushButton

(Obviously, you can change these for any others you need.)

Creating an array to keep references to the instances

Next, we will need a data type acting as a storage reference for every instance (in our example, buttons) we want to add to our UI layout. For this, nothing is better than adding a Propertyto our class whose data type is an array storing objects from our class. With the created class still selected, add a new Shared Propertyfrom the Insertmenu and use the Inspector with the following values:

  • Name: ClassButtons()
  • Type: MMButton
  • Scope: Private

The fact that this is a Shared Property means that it (and its stored values) will be available for all the class instances (objects) created from the class; while the regular properties allows us to store distinct values for each class instance.

Another detail is that, in this case, we have limited the Scope for the Shared Property to Private. That means that this will be visible only from the code executed inside the class.

Setting an index for every object

Of course, we need a way to reference every instance created from the class; for example, when we need to send a message to only one of them and not to all the available instances. For this, nothing works better than setting a new Property (this time, a regular or instance Property) and assigning its data type as Integer:

  • Name: ButtonIndex
  • Type: Integer
  • Scope: Private

Knowing how many objects are in use

We also need to know in runtime how many instances we are using. A good way to learn this is using the Open Event, so during the instance initialization it has the opportunity to add itself to the shared array and also to set its own index value.

To do that, add the Open Event Handlerto the class and write the following snippet of code in the associated Code Editor:

Sub Open() Handles Open
  ClassButtons.Append Me
  Me.ButonIndex = ClassButtons.Ubound
  RaiseEvent Open
End Sub

What does RaiseEvent Openmean? Notice that we are setting our class behavior so if we are using the Open Event on it, this Event will not be available for implementation for any of the instances created from the class.

Fixing this is really simple: add a new Event Definitionto the class using the following values in the associated Inspector:

  • Event Name: Open

This way, RaiseEvent Open will call the Open Event to the instance and will let our class user write and execute additional code during the control initialization.

Setting the Behavior

In order to see our example in action, we need to create a method letting us direct the available instances to do something. For this example, we are going to display a simple message whose content is the Caption set to the button index passed as parameter and that has been created from our class.

As is the case with the Shared Properties, the Shared Method is the resource provided by the Xojo language that lets us set a behavior for the Class itself, instead of being called from a concrete class instance. So, add a new Shared Method to the class using the following values in the associated Inspector:

  • Method Name: SayHello
  • Parameter: Index as Integer

Next, write the following line of code in the Code Editor for the Shared Method:

if ClassButtons.ubound <> -1 and index >= 0 and index <= ClassButtons.Ubound then MsgBox ClassButtons(index).Caption

Testing the Class

We are set to test the class. For this, drag and drop as many instances of the class as you want from the Navigator to the Window Layout Editor (select previously a Window!). For our testing app, we also need an additional PushButton (it can be one from the standard Library) and a TextField.

The TextField will let us to write the indexnumber whose instance we want to send the message to, while the additional button will be in charge of calling the shared method from the class, passing the index value as parameter. Add the Action Event Handlerto the regular PushButton placed in the Window1 Layout, and write the following line of code:

MMButton.SayHello( TextField1.Text.val - 1)

As you can see, when we call the method we are using the name of the class and not the name from the instances.

Of course, this technique not only works when you add the UI controls to a Windows, it also works when they are placed on a ContainerControl that you want to use in your Windows layouts.

Leave a Reply

Your email address will not be published. Required fields are marked *