Subclassing vs Extends vs Containers

Xojo is really powerful because it gives you multiple ways to create really powerful controls.  Today I’ll discuss subclassing, extends, and containers.

Subclassing

First, subclassing a control is really easy to do.  Create a new class and set the super to the control you are extending.  A great example of this is the Xojo ListBox.  In its raw form it’s powerful, but limited, in its functionality.  To have those awesome alternating row colors we subclass the ListBox and put our code in the CellBackgroundPaint event.

One of the things that people forget to do when subclassing the control is to repeat the events that that they are using.  Because I’ve used the CellBackgroundPaint event it’s not available to any instances of the class.  Sometimes this is desirable, but sometimes you want the control to still have those events.

The solution is simple.  Define a new event named CellBackgroundPaint and use the same properties and return value.  This means that you, the developer, can have the alternating colors in your listbox but also still have the opportunity to override those colors if you need them.

The other powerful thing about subclassing is that you can continue to subclass them all you want.  In one of our projects we have the following Listbox hierarchy:  ListBox -> AlternatingRowListbox -> CustomCellListbox -> RecordsetListbox.  Each one is still a Listbox, but each gets all of the events and properties of its supers so this means that the RecordsetListbox automatically gets the alternating rows even though it specializes in loading the listbox by using a Recordset.

Extends

Sometimes, though, you only want to add a single method to a class.  Creating a subclass to do this might be overkill, especially if you have to go back and and retrofit this change in a lot of places.  A good example of this the PopupMenu where you often have to set the text of the PopupMenu to some value.  The code is often like this:

for i as integer = 0 to pm.ListCount-1
   
   if pm.list(i) = sSomeValue then
      
      pm.ListIndex = i
      
      exit
      
   end
   
next

It’s not hard but after you’ve done this several dozen times you come to the realization that this should be automated somehow because it’s a lot copying and pasting and if you find a better way in the future you’ll hate yourself (trust me, I’ve done this).  You could easily subclass the PopupMenu and go through and change the Super of all existing popupmenu’s, but there is an even simpler way of doing this:  Extends

Extends lets you create your own method that acts like it’s part of the class.  If we were to take our code above for the PopupMenu and put it into a module (it won’t work in class objects like Windows) our code would look like this:

Sub SetText(extends pm as PopupMenu, assigns s as String)
   
   for i as integer = 0 to pm.ListCount-1
      
      if pm.list(i) = s then
         
         pm.ListIndex = i
         
         exit
         
      end
      
   next
   
End Sub

Same code, just using the extends.  What’s great about this is that using it super easy.  It becomes part of the AutoComplete list and is a ‘dot’ extension (just as if it was a subclassed item).  So calling it is like this:

pmFont.setText = sTheFont

I don’t remember when the Extends method was introduced but since it was introduced the number of Subclassed items in our projects has dropped dramatically.  Using Extends fails in one area and that’s Properties.  If you need a property for the additional functionality, you’ll still need to subclass the control and add the property to the subclass.

Containers

This final method may confuse some people.  Using a container control isn’t a subclass nor using the extends.  Containers are incredibly powerful controls in Xojo, though, because they allow you to create combinations of controls (and their subclasses) in unique and reusable ways.  A great example of this is a typical Save/Cancel pair of pushbuttons on a dialog or window.  On Macintosh OS X the Save button is on the right and the Cancel on the left.  On Windows it’s opposite.  In Linux, it’s the same order as Windows but the buttons have a different height.

There’s nothing stopping you from adding code to every window that has this Save/Cancel combination so that they’re in the proper location and height.  But why?  This is Xojo, a modern object oriented language!  Plus, I hope you’re as lazy of a programmer as I am because that’s a lot of work.  Use a container!

Create the container, add the pushbuttons.  I named my buttons Left and Right because their function is determined at runtime depending on the platform.  I add an Open event and in that event I add code like this:

#if TargetMacOS then
   
   btnRight.caption = kSave
   
   btnRight.Default = true
   
   btnRight.cancel = false
   
    
   
   btnLeft.caption = kCancel
   
   btnRight.Default = false
   
   btnLeft.Cancel = true
   
#else
   
   btnRight.caption = kCancel
   
   btnRight.Default = false
   
   btnRight.cancel = true
   
    
   
   btnLeft.caption = kSave
   
   btnRight.Default = true
   
   btnLeft.Cancel = false
   
#Endif

The kCancel and kSave are dynamic constants holding the proper strings.  In the ActionEvent of the buttons I have code like this to raise an event to tell the parent container (a window or another container) what happened:

Sub Action()
   
   //btnLeft
   
   #if TargetMacOS then
      
      //Cancel
      
      RaiseEvent Action false
      
   #else
      
      //Save
      
      RaiseEvent Action True
      
   #Endif
   
End Sub

Sub Action()
   
   //btnRight
   
   #if TargetMacOS then
      
      //Save
      
      RaiseEvent Action true
      
   #else
      
      //Cancel
      
      RaiseEvent Action False
      
   #Endif
   
End Sub

And finally the Action event which I’ve defined is:

Event Action(bSave as boolean)

It’s really as simple as that!  No matter which platform I’m on the Action event from this container tells me if the user pressed save or cancel.

Why did I include Containers in this article?  Good question!  It’s because you can think of Container Controls as a way to subclass Windows or WebPages without it being a window/page.  You can reuse Containers as many places as you want and when you change the original it changes the children (be aware though that there are some caveats with this statement – especially with WebContainers).  And even better is that you can dynamically add and remove containers at runtime.  Adding them is simple by using the EmbedWithin commands and to remove them use the Close method (I always set their visibility to false first).

We use a LOT of containers in our consulting work.  We find that it tends to simplify the code (remember being a lazy programmer isn’t a bad thing).  We’ve had windows/pages that have thousands of control and hundreds of controls visible at any given time but with a UI that complex we like to break them out into Containers.  So rather than having a Navigator with a listing of thousands of controls (and their labels!) we break it out logically into containers and the Navigator has maybe a dozen containers instead.

Each container has Save, Load, Validate set of methods (sometimes this is using an Interface) and whatever other methods are required but the point is that the overall window/page doesn’t need to know, or care, what the individual controls are and how to load, save, and validate their data – it only has to know enough to call the Load, Save and validate methods in each container.

With Xojo for Web, containers are even more important because you can create ‘repeating rows of controls’ that are very complex.  We wrote an entire accounting application for a client in using Xojo for Web and as you can imagine creating an Invoice Line Item row that needs Qty, Price, Amount, Sales tax and the necessary UI to add and delete the row, void a row, make it non-taxable can be complex.  Sure, we could have come up with a UI that doesn’t have that in a list, but frankly, it’s not that hard using WebContainers.

Containers are very powerful things and a great way to combine controls into one unit.  It’s also a way to create a dynamic control that can easily be added a runtime.  Rather than using a ControlSet (i.e. control array) and cloning an existing control (and having to keep track of the index) you can dynamically place your container that holds your one control.  We don’t use this technique very often but it does come up every now and then.

Conclusions

Subclassing a control in Xojo is pretty easy but sometimes overkill.  Using the extends method works great if you’re adding methods to a class/control and don’t need to add any properties.  Containers are powerful constructs that let you combine controls into a reusable object that have their own events and that can be added dynamically.  As a Xojo programmer you should be familiar with all three methods.

Did I forget anything?