Adding the same features to multiple control types
Normally it's not a big issue having only single inheritance in the .NET languages. Unless you want to add a set of common features to the controls that Microsoft gives us...
I ran into this in a previous project where we rebuilt an existing application in .NET/C#. One of the features of this application was that controls like TextBox and ComboBox could have different colors depending on the state of certain data. For example: if it was a new entry some fields would have a green background to show that they were mandatory; others were yellow or white. As the state changed (i.e. fields were given a value), their controls switched colors too...
Well, you could of course code this into all Forms (yuck...that's no design), create your own control library (are you out of your mind?), convince your customer that this behavior is something you've never seen before and is impossible to build in .NET...or remember the days of VB6 and its 'inheritance' of classes.
In VB you could use the properties and methods of a class A in another class B by adding a member of type A in class B and make this one publicly visible with a property.
' Class A code
Private Sub Class_Initialize()
' Constructor of class A
End Sub
Private Sub Class_Terminate()
' Destructor of class A
End Sub
Public Sub CoolFeature()
' Do some cool stuff
End Sub
' Class B code
Private m_instanceOfA As ClassA
Private Sub Class_Initialize()
' Constructor of class B
m_instanceOfA = New ClassA
End Sub
Private Sub Class_Terminate()
' Destructor of class B
End Sub
Public Property Get instanceOfA() As ClassA
Set instanceOfA = m_instanceOfA
End Property
Then you could do things like Dim x as B and call x.instanceOfA.someFunctionOfA
' Application code
Sub Main()
Dim b As ClassB
Set b = New ClassB
Call b.instanceOfA.CoolFeature
End Sub
With this in mind, the idea of Extension classes was born. An Extension class is responsible for implementing some common features. Further, you can inherit from an Extension class to extend it. It look a bit like a Decorator pattern (http://www.dofactory.com/Patterns/PatternDecorator.aspx), except that you don't create the Decorator classes yourself, but your derived control class does in its constructor.
I created a tree of Extension classes starting with:
- an abstract base class ExtensionBase as the root class with general properties and behavior (like storing a reference to the Control)
- an Extension class for implementing common features for all control types (like setting colors, bordertype)
- a TextExtension class derived for specific TextBox related features (like regular expression validation on leaving the control)
- a CheckBoxExtension class to translate values coming from a datasource into check or unchecked state and back
- a MenuExtension class for menu-like controls (we used an ExplorerBar control with expanding panels and clickable items to start tasks...like the XP UI)
- and so on.
In the constructor of an Extension class the control is passed as Control type. The instance of the Extension class stores this reference internally in a private member. This way the Extension class instance can invoke methods and use properties of the control in which it sits as a member (well, most of the time you have to cast to the appropriate control class type to do that but why is the 'is' operator invented if you're not gonna use it...)
The controls we used were derived from the control class of the Framework to inherit the default behavior. They also got one or more Extension class members to add new behavior. Finally, by adding public properties and methods to the derived controls classes, the extra features were exposed publicly so that you can program something like
MyTextBox t = new MyTextBox(); t.SomeNewCoolFeature();
These properties and methods simple route to the Extension class member inside to do the job. Also, some events (like Validating) of the derived control classes were linked to a method on the Extension class to implement common behavior for them at 1 place.
Now why didn't I use the Framework's extension possibilities. There is an example which builds a HelpLabel control. Well, I looked at it and decided it would be too much work to do it that way, because your control class has to implement an interface and then the implementation of the extra features still sits in each control's implementation class. I could have put that in a normal class and route to that but what's the point of putting the interface on the control class then?