DekGenius.com
[ Team LiB ] Previous Section Next Section

6.1 When to Inherit

As mentioned in the previous chapter, inheritance is not always the most appropriate form of reuse. There are two ways to create a control class that, from the end user's point of view, looks and behaves just like an existing control. You could either inherit from the control, or simply create a UserControl that contains it. Both approaches are equally easy. The decision must be based on which is better software design for your control.

There are two important points to understand to make this choice. The first is that the decision to inherit is something that affects your control's programming interface, not its user interface. (Recall that in the last chapter we saw that all controls have two sets of users: programmers and end users.) The second point is that inheritance defines an "is a" relationship—if your control inherits from, say, ListView, any instance of your control is by definition also an instance of the ListView class.

Sometimes this will be what you require. For example, you might decide to write a text box that automatically filters its input to certain characters, For instance, you could create a TelephoneTextBox that only allows digits, hyphens, spaces, and the + symbol. In this case, it would be reasonable to use inheritance, because your control is just like a TextBox from the developer's point of view; it just has some special modified behavior from the end user's perspective. There is no problem with letting the developer do anything to a TelephoneTextBox that they might also do to a normal TextBox, in which case we may safely declare that our TelephoneTextBox is a TextBox, as in Example 6-1 (which contains the C# version) and Example 6-2 (which contains the VB version).

Example 6-1. A derived control in C#
public class TelephoneTextBox : System.Windows.Forms.TextBox
{
    private string accept = "0123456789 +-";
    protected override void OnKeyPress(
      System.Windows.Forms.KeyPressEventArgs e)
    {
        if (accept.IndexOf(e.KeyChar) < 0)
        {
  // Not a valid character, prevent
  // default handling.
  e.Handled = true;
        }
        base.OnKeyPress(e);
    }
}
Example 6-2. A derived control in VB
Public Class TelephoneTextBox
       Inherits System.Windows.Forms.TextBox

    Private accept As String = "0123456789 +-"

    Protected Overrides Sub OnKeyPress( _
      ByVal e As System.Windows.Forms.KeyPressEventArgs)

        If accept.IndexOf(e.KeyChar) < 0 Then
  ' Not a valid character, prevent
  ' default handling.
  e.Handled = True
        End If

        MyBase.OnKeyPress(e)
    End Sub

End Class

The general rule is that if you want the public API of the control you are reusing to be accessible to developers using your control, inheritance is likely to be the right solution. Programmers will view your control as being an extended version of the base control. But if you want the control you are reusing to be visible only to the user, and not to the developer, reuse through containment is the best answer.

A very common case in which inheritance is not appropriate is if the purpose of your class is to provide a visual representation of something, such as a list of items in an email folder. Such a control might well be based on a ListView, but it would not want to expose the ListView API. That would permit all sorts of operations that would most likely not make sense, such as the ability to add new column headings or change the view type; those are normal operations for a ListView, but they do not have any obvious meaning for a view of a mail folder's contents—what exactly would it mean to add a new column, for example?

Sometimes, there will be gray areas. To see why, let us look again at the TelephoneTextBox. It is not necessarily true to say that it can be used in exactly the same way as a TextBox: its Text property presents a problem. The control is only supposed to be able to hold a telephone number, and it enforces this by filtering the user's input. But what if some code that uses this control attempts to set the Text property to a value such as "Hello," which is not a valid phone number? The code in Example 6-1 or Example 6-2 does not deal with this, so the control's text will be set just like it would be with a normal text box. This breaks the intent of our control—it is only supposed to hold phone numbers. We could fix this by overriding the Text property and throwing an exception if it is set to an invalid value, but that would then break the "is a" relationship—there would then be things that you could do with a TextBox that you couldn't do with a TelephoneTextBox.

Purists would argue that you should always stick to the Lyskov Substitution Principle: you should only use inheritance if your class can do absolutely everything that the base class can. However, this can often be unduly restrictive. There are plenty of examples in the Windows Forms libraries where derived classes remove functionality that is present in the base classes. For example, the Control class supplies Click and DoubleClick events, but derived controls can disable these if they want to (e.g., the TreeView doesn't raise Click events). So in practice, it is often not a problem to bend the rules slightly.[1] But if you find yourself creating a control that is programmatically incompatible with its base type in many ways, inheritance is almost certainly the wrong choice.

[1] With sufficiently careful documentation, it is even possible to do this and conform to the Lyskov Substitution Principle—your base class must simply document all the variations in behavior that derived classes are allowed to introduce.

Visual Studio .NET has special support for inherited forms. Forms are controls like any other visual element (i.e., they inherit indirectly from Control), and the rules mentioned so far apply just as well to forms as to any other class. However, the way that forms are normally used means that inheritance is more likely to be the right choice for forms than it is for most other control types.

6.1.1 When to Inherit from a Form

Forms inheritance is very useful if your application needs to present several similar dialogs. You can define a base form that contains the features common to all your windows, and then create specialized classes for each variation.

If you derive from a form, your derived class will represent a window that has all the same controls on it as the base class, and you may optionally add more controls of your own. You will only be able to modify the controls on the base class if they have the appropriate protection level. (The relevant levels are protected or public, or for forms in the same assembly as yours, internal in C# or Friend in VB.) The Forms Designer in Visual Studio .NET supports visual editing of inherited forms. It displays controls from both the base class and the derived class, but only allows you to edit the properties of controls to which your class has access—i.e., to those specific to your derived form, and those on the base form with an appropriate protection level.

Forms are more often a suitable candidate for inheritance than other controls, because forms often add little or nothing by way of programming interface to the Form class. They usually stand alone as top-level windows, rather than being contained within another control, and this self-contained nature means that they have no need for much of an API. The only interface they have that matters is the user interface. Because most of the problems associated with derivation revolve around the implications of inheriting the programming interface, this means that forms are usually a safe bet for inheritance.

There is another reason you might use inheritance with forms more often than with controls. Chapter 5 discussed a reuse technique that provides an alternative to inheritance—wrapping the control to be reused in a UserControl. This technique is not available to forms—you cannot contain a whole form inside a UserControl. If you want to base one form on another, inheritance is your only choice (not counting everybody's favorite code reuse mechanism: the clipboard).

Note that Visual Studio .NET also has special support for inheriting from controls based on UserControl. The main reason for this is that Form and UserControl are very similar—they are both composite user interface elements (i.e., they consist of a collection of child controls). The development environment uses the same editor for forms and user controls, so it is not surprising that it supports visual inheritance for both. However, controls based on UserControl are used very differently from forms—controls are designed to be contained by other UI components, but forms normally stand alone. This means that for a UserControl, the programming interface is usually much more significant than it is for a Form. So although visual editing of inherited components is supported for both forms and composite controls, it is usually more useful for forms.

    [ Team LiB ] Previous Section Next Section