Creating custom controls

Xojo makes it possible for anyone to create custom controls for their projects. And its not really that hard. Yet many people don’t bother to take advantage of this capability and then try to place many independent instances on a layout and make several controls behave as though they are a single control.

This quick tutorial is aimed at trying to convince you that it really is worth investing the little bit of time required to create your own custom controls and then reuse them as many times as you want.

Often you can get away with just creating a new subclass of which ever control you want and then customizing how it behaves. Maybe you just need some custom drawing in a listbox. It may not make sense to create anything but a new subclass in that case.

In this example because we want to create something that does not exist and is a composite of other controls we are going to start with a CONTAINER CONTROL.

At this point, depending on what control you want to create you would add which ever controls you need to your container control.
In this example we want a label and a canvas because we’re going to make something that is like the color picker seen in the inspector. Make sure that when you place them on the container you set their SCOPE is set to PRIVATE.

Personally I always do this as code outside the container should NOT reach in and manipulate the controls in our container directly. If you find you think you need something like that add a method or event to make it possible for code outside to get a reference to the control or set a property instead.

Make sure you give the container control a good name as this name WILL show up in the library *. Use a descriptive name for your custom control. Naming it “fooControl” probably isn’t useful unless it literally has something to do with “foo” – whatever that might be. In this case since we’re making a “labeled color picker” we’ll name it labeledColorPicker

Now we’ll add properties that can be set in the inspector that will influence how our control works. We’ll add a label property, as string, that will be the text show in our control. As well we’ll add a currentColor property, as Color, that will be the current color the color picker shows in the canvas. Both of these should be added as COMPUTED PROPERTIES.

We will need a variable to store the value for our currentColor property that we can hold the value in and that we can return when the value is queried. Since we’re using a canvas for this purpose and it does NOT have a property we can use we will need to add a PRIVATE property to our ContainerControl to hold this. Add a PRIVATE property, mCurrentColor as Color, to the labeledColorPicker container control.

In the GET portion of the currentColor computed property put in the code
return mCurrentColor
In the SET portion of the currentColor computed property put in the code
mCurrentColor = value

The LABEL DOES have a property we can use so we aren’t going to need to add a property to our custom control. WE can make use of the LABEL’s text property
In the GET portion of the label computed property put in the code
return label1.text
In the SET portion of the label computed property put in the code
label1.text = value

Now to make it so we can expose these two custom added properties we need to RIGHT CLICK the labeledColorPickerControl in the navigator (the left hand list in the IDE)

In the contextual menu you should see an option for “Inspector Behaviour”. Select that item and you will a panel that will allow you to customize which properties are seen in the inspector.

**** WARNING **** DO NOT DISABLE ANY check boxes UNLESS you are using TEXT or XML projects as in binary projects getting them re-enabled is VERY difficult (see **)

To make a property, including computed properties, visible make sure the check box at the left is ENABLED

Now if you drag an instance of your custom control to a layout you will see the properties we just exposed in the inspector along with all the others the container normally has.

Note that at this point our control is NOT making full use of our new properties yet. Due to how the IDE works it does NOT give you a full WYSIWYG experience for custom controls. It does NOT execute your custom code for setting the newly added properties when it draws the layout. Changing the label text property or the currentColor will not redraw the IDE’s representation using those values. You MUST run the project to see any changes.

Lets make it so when the currentColor is changed that we see this change in the control at runtime. In our labeledColorPickerControl we’ll edit the SET method for the currentColor computed property. What we are going to do is SET the mCurrentColor property, which is already in place, AND make sure that the canvas shows this color change by making it redraw itself.

Make the currentColor SET method in the labeledColorPickerControl read as follows
mCurrentColor = value

As well we need to add the PAINT event to the canvas control on the labeledColorPickerControl so the color is shown. Add the PAINT event to Canvas1 and in that event put
g.ForeColor = mCurrentColor
g.FillRect 0, 0, g.width, g.height

Now if you change the currentColor in the inspector & run you will see that color used in the canvas at runtime. And if you put code elsewhere that changes the currentColor of this custom control you will also see the change reflected at runtime. The same is true if you were to change the label text.

Congratulations – you have just created your first custom control. More complex examples abound but the principles are still much the same regardless of whether you start with a Canvas subclass that draws itself specially, or something more complex that uses a container control for a more complex UI element.

Foot Notes

(*)you can hide things from the library
if, for some reason, you DO NOT want you control to show in the library you can add an attribute to your control called HideFromLibrary and it will not show

(**) Note that IF you disable one getting it re-enabled is VERY difficult because of a bug in the IDE

5 Replies to “Creating custom controls”

  1. I’m still getting the hang of ContainerControls, so thank you for this Norman.

    One thing I discovered recently about the “Inspector Behaviour” that makes designing the UI a little more helpful is the Width & Height properties. For example when making a button from a Canvas (or subclassing something that doesn’t have a design view).

    By default, a Canvas size is 100px by 100px. And when dragging the custom button into the Window design view, that big square can be annoying when you’re trying to fit your new button into a small space.

    If the custom button is always going to be, say, 24px by 24px, then setting the Width & Height in the Inspector Behaviour dialog means the dragging overlay image is also 24px square and much easier to place in the UI (and without inadvertently parenting to the wrong control too).

    I wish I had known that earlier I can tell you 😉

  2. Yes
    Any existing value in that pane can be altered – but be careful with them
    The values in that pane are the default values that a new instance will have when you drag it out to the layout

  3. Re: Warning

    ContainerControls seem to at least partly keep the state they were in when you dragged them onto the window.

    So whenever I changed or mucked up a ContainerControl then the easiest way I have found to rectify the situation is to drag a new instance of the ContainerControl onto the window.

    1. Once you place an instance of just about any control on a layout changes to the original base class are usually NOT replicated to the existing instances.

      Basically this is because once you do place the instance it has user set values, even if you didn’t alter them, and the base class Inspector Behaviour does not overwrite user set values. Imagine if you dragged one out, changed its height and width and then altered the base class values for the default height and width. If that caused every existing instance to have its height & width reset to those new values in the inspector behaviour you’d have to go alter every one back to what you had set.

      That would be less than ideal.

  4. I’ve done some custom controls in the past, but this explanation makes it so clear. The next one I make will definitely do everything you suggest. Thanks!

Comments are closed.