Creating Your Own Controls, Part I: Introduction...

Developing your own controls is a simple process. It usually doesn't take much effort (depending on the complexity), and can be rewarding in the end.


By: Brian Mains Date: April 20, 2004

Developing your own controls is a simple process. It usually doesn't take much effort (depending on the complexity), and can be rewarding in the end. At a bare minimum, creating your own control looks like this:

Public Class MyControl
  Inherits WebControl
End Class

Obviously, there is more to it than that, which is where this document will begin. When creating your own control, you have to determine if:

  1. A class exists with all of the functionality that is desired and can be inherited from
  2. A control is needed that contains a combination, or composite, of controls

How you implement either one is fundamentally different from the other, as you will read below. The ultimate goal for developing your own control is to create something with better functionality, performance, or usability. This document will explain the process and theory for creating a custom control. Future article(s) will be a technical walkthrough an example of building such controls.

To implement a custom control, it is easier to implement from a base class control object, or an existing web or HTML server control. Some of the classes you can inherit from are:

These are generic classes that your control can implement. If you are looking for more specific features in a control, but don't want to inherit from a specific control directly, there are other classes that can be used to inherit more specific functionality, such as:

Lastly, the other option in creating controls is to inherit from the control directly.

Properties, Methods, and Events

It is possible to add customized properties, methods, and events to the control; however, the level of scope must be determined first. Most properties and events will typically fit under the public scope because these are what the developer/user will need access to, not just the application only. Methods, on the other hand, typically will be declared public and protected. Protected methods can still be accessed within the control itself, as well as any controls that inherit from it. This is useful for helper functions that users don't need to access, but the derived controls can still take control of.

Let's examine custom properties first. Creating a property typically involves defining a private variable to maintain the state of the property during assignment. Additionally, attributes can be defined to provide additional documentation/functionality within the Visual Studio .NET designer. Such attributes (defined in the System.ComponentModel namespace) may be:

Bindable – Specifies whether this property can be bound when binding data to the control
Browsable – Whether the property can be modified in the designer
Category – The category that the property will appear under in the property window when not in "A-Z" mode
DefaultProperty – The default value for the property in the designer; typically, the default value appears as normal text in the property window; any other value is bolded
Description – The description of the control that appears in the property window
DesignOnly – A Boolean value indicating that the property can or cannot be set at runtime (design-time is always an option) in the property window

There are many more attributes that can be assigned to properties. It may also be important to note that when researching these attributes, their names have a suffix of "Attribute" (BindableAttribute, BrowsableAttribute, etc.). These attributes are added at the beginning of the property definition; for example:

Private m_strProperty As String
<Bindable(True), Browsable(True), Category("Misc")> _
Public Property MyProperty() As String
...
End Property

Custom methods can be added to a control to handle any desired functionality as needed, as well as events. There are multiple ways to handle event declarations, which both of the declarations below create the same event:

Public Event MyEvent(sender As Object, e As EventArgs)

Public Event MyEvent As MyEventHandler
Public Delegate Sub MyEventHandler(sender As Object, e As EventArgs)

The latter approach uses a delegated function to handle the event declaration. Any class inheriting from the control can now use the MyEventHandler delegate in their event declaration.

Event handlers can be also declared via AddHandler method, which takes two parameters: the first is the event that the handler will be triggered on, and the second is a delegated function for this event. This is very useful when creating controls at design time. For an example, let's use the LinkButton control, which is created at design-time. The AddHandler method is used as follows:

AddHandler MyLinkButton.Click, MyDelegatedFunction

Everytime the LinkButton is clicked, the MyDelegatedFunction method is called. This method takes the same parameters that the event declaration in a web form would have.

Composite Controls

Composite controls are a combination of two or more server "child" controls, lumped together into a "parent" control. The server controls are created at runtime using the CreateChildControls method. This method can implement any number of server controls at runtime, which are added via the Controls collection property.

The exact time of execution of the CreateChildControls method cannot be determined. If any properties or methods rely on this, the EnsureChildControls method or ChildControlsCreated property must be used to verify that the CreateChildControls method was called.

For any controls that have events important to the composite control, events can be received from the child control via the AddHandler method and the use of a delegate (as described above). This is declared in the CreateChildControls method. Also, the RenderChildren method handles the rendering of each child control in the Controls collection. This method is usually called from the Render method.

INamingContainer

The INamingContainer interface assists with creating unique names in ASP.NET web forms. It does this by versioning the control ID's when a conflict occurs. For example, a form has three TextBox controls with no ID values specified. The INamingContainer interface ensures that the first one is named TextBox1, the second one is named TextBox2, and the third one is named TextBox3. This is important to define in composite controls, because you are dealing with more than one control, and it can become a hassle to specify ID's for every control.

Custom Controls

Custom controls go above and beyond the standard .NET framework server control. After inheriting from the server control, the custom control can contain additional properties, methods, or events, as well as override or shadow existing ones. Depending on the amount of control desired, the Render method can be overridden to customize the output to the browser. By overriding, overloading, or shadowing existing methods or properties, additional functionality can be gained. For example, by shadowing the ToString function to return data in a widely-used format can increase the potential in your control.

Design Considerations

In developing controls, the determination needs to be made over the use of client-side versus server-side code. When at all possible, use client-side code. There are not as many performance issues and there are no server round-trips involved. However, the .NET framework generates code based on browser type and version, whereas with client-side code, the control makes that determination. Lately browser type and version isn't as much of an issue as it once was; new versions of Netscape and Internet Explorer are compatible with most of what the W3C has to offer. Depending on requirements, browser compatibility is something to think about.

Also, a good candidate for a control is where it could improve code reuse. Anytime code reuse is a possibility, it is a very wise decision to do so.