Developing Custom Windows Controls Using Visual Basic .NET

 

Duncan Mackenzie
Microsoft Developer Network

May 2002

Summary: An overview of associated sample articles illustrating the basic concepts of Microsoft Windows control development through both discussion and code snippets. (7 printed pages)

Download WinFormControls.exe.

This overview is the first in a five-article series on developing controls in Microsoft® .NET:

Contents

Introduction
Why Develop Your Own Controls?
How Inheritance Has Changed Control Development
Resources
Samples
Summary

Introduction

One of the biggest selling points of Microsoft® Visual Basic® has always been its support for components, allowing third parties to develop visual controls (and eventually, non-visual components) that provided new functionality to the Visual Basic developer. This aspect of Visual Basic development led to the availability of a huge number of controls—both shareware/freeware and from third-party development shops. Eventually, new visual and non-visual components could even be developed directly in Visual Basic, allowing for the development of an even larger number of controls, many of which where created by programmers (and programming teams) for their own use.

Note   If you or your team is currently using Microsoft ActiveX® controls, moving to Microsoft .NET doesn't have to mean upgrading or rewriting. Using either the Microsoft Visual Studio® .NET integrated development environment (IDE) (on the Tools menu, select Customize Toolbox) or the .NET Framework utility ActiveX Control Importer (Aximp.exe), ActiveX controls can be used from within .NET applications. If a particular control does not work in .NET, it may need to be upgraded by the component's author. If you are working with controls purchased from a third party, then check the vendor's Web site for an update to the control or a .NET version.

In the world of .NET development, the need for custom UI components is still present, but the mechanisms for creating these components have changed. In this article, I will discuss why you would want to create Microsoft Windows® controls, how control development has changed from Visual Basic 5.0 and version 6.0, and I will introduce four samples (each covered in its own article) of control development.

Why Develop Your Own Controls?

If you wanted to restrict the type of text that could be entered into a TextBox on a Windows Form, you could go into the code of your form and create a procedure to handle the KeyPress event in the TextBox. In that event procedure, you would then check the key that was pressed and decide whether it should be allowed:

Private Sub TextBox1_KeyPress(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.KeyPressEventArgs) _
        Handles TextBox1.KeyPress
    If Not Char.IsDigit(e.KeyChar) Then
        e.Handled = True
    Else
        e.Handled = False
    End If
End Sub

Note   Trapping key presses will not ensure that only digits are entered, as the user could paste a value in, or the TextBox could start out containing invalid data. Other events, such as TextChanged, cover more possibilities, but personally I prefer to perform the check when the user leaves the input control, using the Validating or Leave events. Checking only when they leave the control removes the immediate feedback that you would get by blocking invalid keystrokes, but it allows the user to paste in text that is "slightly invalid," such as "3425 2343 2342 2342", into a field that doesn't allow spaces, and then fix it up before leaving the control.

Adding code to the event of a control isn't particularly hard, but what happens when you start doing more difficult programming, such as validating a postal address or a vehicle's VIN #? You will likely want to use that code on several forms, perhaps even in several projects, and you may want to share your work with other developers on your team. Distributing the code snippets from your form, along with instructions on control naming and setup, will start to be a real pain. Developing your own control is a way around all of those distribution problems, as it builds the user interface and the associated code into a single component that you can distribute relatively simply. Updating the functionality of that control can now be done with a redistribution of your component's assembly, instead of sending out new code snippets and asking developers to paste the new code over the old!

How Inheritance Has Changed Control Development

Control development in .NET is very different than it was in Visual Basic 6.0, and a large part of that difference is due to the availability of inheritance in .NET. Using Visual Basic 6.0, you were essentially limited to doing your own drawing and/or using a combination of other controls to create the desired functionality. If you wished to create the custom text entry box described earlier in this article, then you would create a new ActiveX control that contained a TextBox within it.

Note   This process is often referred to as containment and delegation, and is actually used in non-control classes in Visual Basic 6.0 as well when you want to simulate inheritance.

Your new control wouldn't have any of the expected properties of a TextBox (such as a Text property) unless you added them yourself, and you would even have to add code to ensure that the TextBox took up the entire area of the user control and to handle resizing. You could certainly accomplish your goal, and with the help of the ActiveX Control Interface Wizard (a Visual Basic 6.0 add-in, see Figure 1), it wasn't that difficult of a task, but to accomplish the same result in .NET you follow a completely different process.

Figure 1. Visual Basic 6.0 included an add-in to simplify control development by automatically adding and mapping control properties.

Inheritance allows you to avoid duplication when creating your own control by allowing you to build on the functionality of any other .NET control. To create your own TextBox control, you will create a control that inherits from the existing TextBox control, instead of from UserControl. The base control, which you inherited from, provides you with all of its functionality so that you only have to code those aspects of the control that differ from the base. Taking a look at this in practice, here is the code required to create your own TextBox control that allows only numeric data to be entered.

Note   If you want to try this code out for yourself, simply create a new Visual Basic .NET project, using the Windows Application template, so that you have a blank form to try your new control on, and then add a new class (named NumericTextBox) to your project. Replace the code in the new class file with the code listed next, and then build your project. After it has been built, you can add it to your toolbox. On the Tools menu, click Customize Toolbox and browse to the .exe created from building your project:

Public Class NumericTextBox
    Inherits System.Windows.Forms.TextBox
    Protected Overrides Sub OnKeyPress(ByVal e As _
           System.Windows.Forms.KeyPressEventArgs)
        If Not Char.IsDigit(e.KeyChar) Then
            e.Handled = True
        Else
            e.Handled = False
        End If
    End Sub
End Class

Yes, that really is all the code you need, and if you are really picky, it could even be coded in fewer lines through the magic of Boolean logic:

Public Class NumericTextBox
    Inherits System.Windows.Forms.TextBox
    Protected Overrides Sub OnKeyPress(ByVal e As _
         System.Windows.Forms.KeyPressEventArgs)
        e.Handled = Not Char.IsDigit(e.KeyChar)
    End Sub
End Class

Without doing anything else, this control displays itself correctly, provides the same events as a TextBox and has all the properties and methods of a TextBox. You can even data-bind to this control without any additional programming, as that functionality is provided by the base control as well.

Note   This control is very restrictive and allows only the digits 0-9 to be entered, which means that numeric data containing commas, decimal points, or even a negative sign would not be valid. I will take a look at a more functional method of validating input in the first sample article. (For more information on the four samples, see the Samples section later in this article.)

The same control built using Visual Basic 6.0 would have almost the exact same amount of meaningful code, but would also have a large amount of utility code to handle resizing the control and exposing selective properties. Although reducing the amount of code you need to type is a great feature, it is only one benefit of building controls using inheritance. A more powerful benefit of inheritance is that your newly created control will work with any code that was expecting the base control type, which allows you to pass your new NumericTextBox into any routine that expects a TextBox control. By extending an existing control, instead of starting with only the UserControl class, you not only gain the functionality of the base control but you also benefit from the familiarity developers have with the base control's properties, methods, and events. Anyone who knows how to work with the standard TextBox can program with the new NumericTextBox without any learning curve. The ability to inherit from an existing class (in this case a control) is one of the more powerful differences between Visual Basic 6.0 and .NET, but it is only one of many. If you follow through the samples later on in this series, you will see that Windows Forms controls contain many powerful features and can be created with relatively little effort compared to earlier versions of Visual Basic.

Resources

To help you get started building your own Windows Forms controls, I have prepared a list of resources covering some of the more common concepts, techniques, and issues.

In addition to the resources listed above, I have written several samples to illustrate some of the more common concepts of control development. I like to classify controls into one of a small group of categories, based on the desired functionality:

  1. Enhanced Controls (minor)
    Making small modifications to the behavior of an existing control, such as the NumericTextBox just described and the Regular Expression TextBox detailed in the Samples section to follow, and adding only a few, if any, new properties, methods, or events. Controls in this category would normally inherit from an existing control (such as System.Windows.Forms.TextBox).
  2. Enhanced Controls (major)
    Building on an existing control, but adding a large amount of new functionality and usually several new properties, methods, and/or events. The data-bound checked ListBox and the data-bound grouping TreeView (detailed in the Samples section to follow) are both examples of this category of control. As with the enhanced controls, controls in this category would normally inherit from an existing control (such as System.Windows.Forms.TreeView).
  3. Composite Controls
    A control that combines several other controls, such a TextBox with an associated up/down control, and involves adding your own properties, methods, and events. The data-bound survey radio button (detailed in the Samples section to follow) is an example of this type of control. Controls in this category should inherit from System.Windows.Forms.UserControl and you should start with the UserControl template in Visual Studio .NET to get started quickly.
  4. Completely Custom
    Controls where you are forced to start from scratch, or any control where you handle your own drawing. The data-bound thumbnail view control (in the Samples section to follow) is only one example of this type of custom control, but a wide variety of controls could fit into this category. Controls in this category would simply inherit from System.Windows.Form.Control.
  5. Extender Providers
    This type of control or component is fairly distinct from the other four categories, as it works by adding functionality to other controls on the same form. The ToolTip, ErrorProvider, and HelpProvider are all examples of this type of control. The process of building one is covered in the first sample article.

Samples

Each of these samples is intended to illustrate specific aspects of control development, including inheritance, data binding, and drawing your own graphics. The samples are all included in a single download, available from the download link at the top of this article.

The first sample article, Adding Regular Expression Validation, covers the basics of inheriting from another control and adding your own properties, methods, and events along with adding your own code. This sample will also cover an alternative method of adding this same functionality, through an extender provider control. The second sample article, Combining Multiple Controls into One, uses a survey radio button control to illustrate how to create a custom control by combining existing controls, and describes how simple data binding works with the controls you develop.

The remaining samples deal with more advanced control development, including complex data binding and drawing your own user interface with GDI+. The third sample article, Extending the TreeView Control, takes you through the development of a relatively complex data-bound control: a TreeView that can display a hierarchy of items based on a data source. The final sample article, Drawing Your Own Controls Using GDI+, details a control that handles all of its own drawing: a data-bound thumbnail view. In addition to the control-specific functionality, this final sample also illustrates basic GDI+ techniques, such as drawing strings, rectangles, and images.

Summary

Custom control development, whether it is for personal use, team development, or commercial distribution, is one of the most powerful forms of component technology. This concept of packaging appearance and functionality together into a redistributable package has been a major driver behind the success of visual development tools such as Visual Basic, and it continues to be a popular concept in current versions of these tools. Control development is greatly changed in .NET, but it is a change for the better, as features such as inheritance simplify the process of customizing existing controls while reducing the amount of code you have to write. The samples described above are designed to give you an introduction to creating custom controls, illustrate some important control development techniques, and perhaps even give you some ideas for your own controls.