Using Your Inheritance
Visual Studio .NET introduced a great feature that was not new to many developers, but was new to Visual Basic developers. It was a feature that greatly increased the options for everyone who previously based their efforts on the COM platform. Am I talking about Web services? Xcopy deployment? The Common Language Runtime (CLR)?
Those are good guesses, but what I am talking about is inheritance. To me, this is one of the biggest benefits introduced by the first version of Visual Studio.NET. In fact, to me, the lack of inheritance support was the main reason I avoided VB programming in the pre-.NET world. It was unbelievable to me that a mechanism so fundamental to object-oriented development was not available. The tides have turned though, and now we have inheritance support and more: cross-language inheritance that represents the foundation of everything in the .NET Framework.
I am happy to see developers using this great feature in many ways and for many different purposes. It seems that increasing numbers of developers understand not just the theory of inheritance, but also know how to use this powerful feature in real-life situations. Yet there is one area where inheritance is often overlooked: inheritance in Windows Forms.
Why is that? Frankly, the thought of re-creating forms that behave like I want them to, re-creating buttons that look like I want them to, and re-creating textboxes that format their input the way I want them to, gives me the heebie-jeebies! To be honest, technically, inheritance is used a lot in Windows Forms, as every new form that gets created is a sub-class of a Form class. But that's not what I am talking about.
Consider a simple scenario. Assume that you need to build a Windows Forms application with a variety of different forms. You might right-click your solution and choose "Add Windows Form..." from the menu. This appears to create a brand new form from scratch, but it really doesn't. Instead, the Designer creates a new class that inherits from System.Windows.Forms.Form, and which looks and behaves very much as you would expect a Windows form to behave. The class makes calls to the Windows API to produce something that looks like a window and a form. The class provides easy access to many of a form's features, such as the ability to set the window's title or to specify whether you even want a title bar. The System.Windows.Forms.Form class goes beyond a bare-bones abstraction of a simple window, and provides features such as integrated localization or the ability to set the "Accept" button.
Everything provided by the basic System.Windows.Forms.Form class is great stuff, and by adding a "new" class to the project that inherits from System.Windows.Forms.Form, you tell the compiler that you want to create a class that is exactly the same. You end up with something that looks and behaves like a form and you can safely proceed to adding additional objects (buttons, textboxes, etc.) and additional functionality to the form. To state it a bit differently: you started out with a generic form that was just like any other form, and only added and changed the things that were exceptional. This is why inheritance sometimes is also referred to as "programming by exception."
So far, so good. But the million-dollar-question is: what are the chances that the basic System.Windows.Forms.Form class fits your needs 100%? Pretty close to 0? How often do you go into your new form and change the same exact property that you have been changing in all the other forms you have ever created, like maybe the icon? All the time? I thought so. So why, I ask, would you keep doing this? My guess is that performing these types of repetitive tasks is not the highlight of your developing day. The solution is simple: use inheritance the same way you use it in other development areas, and use it the same way the Microsoft Windows Forms team used it when they built that part of the .NET Framework.
So how do you use inheritance like that? First you create a Forms class the same way you would when building any new form, because that new form really is a subclass of the default System.Windows.Forms.Form class. Then, change whatever you want to change. Maybe you want to change the default icon. Maybe you want to add some functionality that manages the form's default screen position a bit differently. Maybe you want to render the form's background in a slightly different way. Or maybe, you are happy with the way the form works for the time being, but want to give yourself a nice place to add and change behavior and appearance later.
Then, whenever you create a new form class, instead of picking "Add Windows Form..." from the right-click menu, pick "Add Inherited Form...". You pick the Form class you created from the dialog box. (Alternatively, you can create a default form and change the source code so the new Windows Form inherits from your form). This way, you end up with a new form that is exactly like the Form class you fine-tuned. Your own Form class inherits all functionality from a default System.Windows.Forms.Form class, so you don't have to fear losing any functionality or incompatibility with future versions of the Framework's Form class. You simply have a version of the form that is a bit more powerful than it would be if you were using the "barefoot" version.
But wait: there is more! You now have a more powerful Form class than the basic class, which is a great start every time you build another form. It also gives you great flexibility in case you want to add functionality later. Perhaps you just discovered that your application doesn't work well in a multi-monitor system, for instance. Now you can fix the problem in one place, rather than fixing hundreds of forms individually.
But this is only the starting point. As you develop your application, you will discover that certain types of forms are used over and over again. There will be modal dialog boxes, for instance. There will probably be data editing forms. To make your life a lot easier in these situations, you can subclass the Form class you just created and create more specific sub-classes, such as a DataForm class.
In the Milos Solution Platform (the framework we use internally at EPS--www.eps-cs.com/milos), we have a class with a number of additional features specific to the task of editing data. The class provides simple features, such as form-wide undo capabilities, and sophisticated features, such as multi-threaded data loading and saving. These types of features would be difficult to put into each form individually, and they would be even harder to monitor for quality. But with the specialized class, EPS only developed and tested this type of functionality once, and it is reasonable to assume that this use-tested code is stable and safe to use. All a new form's developer has to do is override the form's Save() method, and all data verification and saving will happen in a multi-threaded environment without any additional worries about thread-safety. Su-weet!
You could follow this path a bit further. I could talk about specialized data forms, such as a default form that allows editing a person's contact information and that you then subclass from and tweak according to your needs. Or, I could tell you about our default Form class used for invoices. Or how about the class we use for Point of Sale systems? But that would just be more of the same. Instead, I want to draw your attention to another area in Windows Forms where inheritance is very important: controls.
Did I just see you dropping a simple textbox onto the Form classes you just created? Shame on you! Individual controls are really no different than the forms themselves. The default classes (controls) provide great functionality, but they are only a rough approximation of what you might need. If you are like me, you have been craving a more sophisticated textbox that provides better support for advanced input masks and validation, as well as data type-specific entry to get better support for numbers or date types. Or how about a data grid that supports a real double-click event and a simple way to get to the selected row? How about a tree that fires its node-click, even when the user clicks on a node that has already been selected? I even fantasize about controls that remember their previous values or perform auto-complete tasks.
If you share my desire for such outlandish features, I have good news for you: subclass these controls, and add these features once and for all, and from that point on, only use your specialized classes, rather than the default ones. You'll be surprised how easy it is, and how seamlessly a new set of controls replaces the default ones.
Ah, and by the way, even if you are perfectly happy with the functionality the default controls provide, subclass them anyway! Once again, this simple design trick gives you a great place to add shared functionality later, or fix undesired behavior in a single place. Just imagine you need to TabletPC-enable your application. Do you really look forward to adding ink capabilities to every single textbox in your application, or would you rather go to a single class to add this feature across-the-board?
So what are the downsides of all of this? In my mind, there are none, but there are a few frequently raised questions. One of them is the issue of performance. How much of a hit do you take by adding an inheritance level or two? Well, there is some, but not much. If you were to instantiate a million textboxes on your form, you should worry. You are not doing that? Then you are probably fine. The fact is that in Windows Forms, you don't end up instantiating a lot of objects in a loop or anything of a similar nature. Therefore, the performance hit is insignificant. Check out the inheritance tree that is already in place for controls such as textboxes and buttons. Adding another level really doesn't matter, as far as your workload.
Another concern is complexity. Doesn't it get terribly difficult to use all this inheritance? Well, have you had trouble creating forms before you thought of them as using inheritance? Did you have difficulty dropping textboxes onto the design surface? No? But surely, using all the properties, methods and events must have given you nightmares then?
The truth is that inheritance simplifies creating interfaces (and other objects). Inheritance also makes quality control much easier. For one thing, it is logistically easier to test a single object. Also, you generally have more time to implement a feature such as advanced textbox input validation for a single class than you have to do this kind of implementation for thousands of individual controls.
Of course, there is the chance of breaking things on a very large scale. What if someone added code to the key-press events that kept any of the keystrokes from getting into the textbox? The answer is simple: the entire application is now completely damaged because no text can be entered anywhere. Therefore, large-scale changes require a little bit of planning and the usage of some basic software engineering principles. The good news is that these types of problems are generally easy to find because they are so wide-spread and often obvious; little bugs that are introduced only for a single control may easily slip through undetected.
At CoDe Magazine, we have been publishing articles covering inheritance for quite some time. Search our online achieves for more technical information and implementation details. You may also safely assume that we will keep publishing technical information about this subject in future issues. Also, stop by my personal Web site (www.MarkusEgger.com) if you still want more. And drop me a line if you are using this technique or have questions about it. Now, go and remove all the references to the default classes and replace them with your own!