More good habits

On the forum there was a suggestion that a person should use a container control that had a few controls embedded in it at design time. And then when using that container control that they should skip providing an API that the container presented and just reach inside the container and manipulate the controls it contain directly.

Personally I would recommend against this.

I’d start by saying when you create a container control ALL the controls in it should be private by default to prevent this. And that if you want to expose functionality of the controls on the container you do so by defining methods and events on the container that any code OUTSIDE the container can call or react to just as if the container control was any other single control and not a composite one like it is.

Why would I make such a recommendation ?

  1. good habits
  2. encapsulation
  3. reusability
  4. long term flexibility and maintainability

The first point is just that this is a good habit to get into. And the reason its a good habit is because of points 2, 3 and 4. Properly encapsulating and hiding the details from other bits of your code is a good thing. Code outside the container doesnt need to know HOW the container does what it does. Just that it does what is expected when you call its methods, change its properties and react to the events it exposes. Thats it. It should be a black box like the built in Xojo listbox, pushbutton, or any other built in control is. You dont need to know how those do what they do, just that they do what you expect when you call the methods, set the properties and react to their events.

And the bonus to doing this is that it makes the likelihood you, or others, can reuse your control in more places in your project or in other projects much higher because the control is self contained.

Long term it also lets you do things like completely swap out the implementation of the container for some other means and as long as you dont need to change the API nothing outside the container control even needs to be aware this has happened. This makes your own code easier to maintain since you no longer have to look through all the code outside of the container to know if you also need to alter it because something in the container changed.

These are all good things regardless of whether this code is for your own use, more general distribution or possibly for sale or to give away.

I’d encourage everyone to keep these things in mind when ever they write their own custom controls.

Bevelbutton redux

At the end of June I posted about a replacement for the bevel button that I’d created.

Since then I’ve had some feedback and some updates & fixes submitted for it and a newer versions has been posted.

If you’ve used it in your projects you might want to grab the latest version.

Keep your code off of my controls

Suppose you are creating a new container control to encapsulate some functionality onto this new control. You’re already thinking about “encapsulation” which is a good thing.

Why, when you create this new container, would you then make all the controls on it public so code outside the container can change it ? That seems like it would be a good way to ruin the encapsulation you’re trying to achieve.

And, if you do this, any changes to your nicely set up container then may require broader changes in your application if, or when, you make changes to your container.

Lets walk through a quick and simple example that illustrates the issue.

Start a new desktop project.

Add a container control to the project – it will cleverly be named ContainerControl1.

Add a Generic Button to this container. It will be called “PushButton1”.

Make sure its scope is “Public”

Now add an instance of this container to Window1.

Add the Open Event to Window1.

In there put

ContainerControl11.Pushbutton1.enabled = false

And when you run the pushbutton on the container will indeed show up as disabled.

Now imagine you add a lot of other functionality to your application and there are several places where your code enables or disables this button under various conditions. And perhaps you reuse this container on several other windows.

And then you decide to make some changes to the container. And one of those changes is to rename the button to something more meaningful.

In the project we’ve built so far this isnt so bad. If we rename PushButton1 to “ContainerButton” and try to run again we’ll get an error saying

Window1.Open, line 1
Type "ContainerControl1.ContainerControl1" has no member named "PushButton1"
ContainerControl11.PushButton1.Enabled = false

Now in a small application this is manageable. You simply hunt for and change the names and off you go.

But, you may have to do this many times depending on how many times you set the enabled state of the button. And you have to update them all.

And none of this was necessary.

If instead of letting code outside the container reach in and manipulate the control directly we provided an API to do this and hid the innards then we could be sure we have fewer places to fix and that no code outside the container isnt somehow changing the setup in some way that will cause other issues.

In the example lets do this.

In ContainerControl1 add a method

SetButtonEnabled(assigns isEnabled as boolean)
  ContainerButton.enabled = isEnabled
End Sub

Make the container buttons scope Private.

Now if you run you will get an error in the Window1.Open event saying

Window1.Open, line 1
Type "ContainerControl1.ContainerControl1" has no member named "Pushbutton1"
ContainerControl11.Pushbutton1.enabled = False

This is because the push button is now private and hidden to code outside the container control.

So we’ll fix this one line to

ContainerControl11.SetButtonEnabled = False

Now if you run the button is once more disabled. But now when we make changes to the ContainerControl we can focus on the container control and the methods it exposes to the rest of our code and not have to worry that we will have to fix a lot of other code elsewhere in our application.

Lets try this out.

Open the container and change the buttons name to “foobar”. And change our method we had for setting the enabled state to

foobar.enabled = isEnabled

And run the sample again.

We made two small changes and no other code had to change. If we had used this container many many times the time savings could have been significant. And the chance for odd bugs caused by code outside the container altering the containers contents significantly reduced.

Make your controls private and expose an API for them and save yourself a lot of grief.