I will start off by discussing about how to create server controls from scratch. There is two classes that you can inherit from – the Control class and the WebControl class. The Control class has no features what-so-ever – it is the essential class that is relied on by the entire user interface in an ASP.NET page.
In a WebControl class, there is additional properties and methods – most of them are protected so the inheriting class can modify the behaviour. The WebControl class is mainly built for rendering HTML a lot easier. Answer the following question below to decide if you should inherit from the WebControl class:
Is the server control going to render one big HTML container to hold all rendering in?
If you answered yes to the question, then inheriting from the WebControl class will be a better option for you to pursue. In my opinion, it is a lot easier to control the WebControl class because the rendering is broken into smaller, logical blocks.
On top of that, it also adds diversity for the page developer to add some attributes during the page life cycle (without you, as the control developer, to add extra code into your control). For example, he might want to add an OnClick attribute to your server control which renders a <table> tag so that the page developer can do his own click event (server-side or client-side – let it be his choice).
To demonstrate the simplicity that the WebControl class can have over the Control class, we will build a simple Label control (without using the built-in controls). The functionality that our Label control must achieve is:
-
Must be able to output text from its Text property or from the control's inline content.
-
The output must be enclosed in the <span> tag.
-
Must have the ability to be classed for formatting from CSS.
For the demonstration to be as simple as possible (and not to overcrowd the code), we will not persist objects to view state. First, we are going to demonstrate with the Control class:
// [C#]
[ToolboxData("<{0}:CustomLabel runat=server></{0}:CustomLabel>")]
public class CustomLabel : Control {
private string pClassName;
private string pText = String.Empty;
public string ClassName {
get { return pClassName; }
set { pClassName = value; }
}
public string Text {
get { return pText; }
set { pText = value; }
}
protected override void OnInit(EventArgs e) {
if ((Controls.Count > 0) && (Controls[0] is LiteralControl))
Text = ((LiteralControl)Controls[0]).Text;
base.OnInit (e);
}
protected override void Render(HtmlTextWriter writer) {
writer.RenderBeginTag(HtmlTextWriterTag.Span);
if (ClassName != null)
writer.AddAttribute(HtmlTextWriterAttribute.Class, ClassName);
writer.Write(Text);
writer.RenderEndTag();
}
}
' [VB.NET]
<ToolboxData("<{0}:CustomLabel runat=server></{0}:CustomLabel>")> _
Public Class CustomLabel
Inherits Control
Private pClassName As String
Private pText As String = [String].Empty
Public Property ClassName() As String
Get
Return pClassName
End Get
Set (value As String)
pClassName = value
End Set
End Property
Public Property [Text]() As String
Get
Return pText
End Get
Set (value As String)
pText = value
End Set
End Property
Protected Overrides Sub OnInit(e As EventArgs)
If Controls.Count > 0 AndAlso TypeOf Controls(0) Is LiteralControl Then
[Text] = CType(Controls(0), LiteralControl).Text
End If
MyBase.OnInit(e)
End Sub
Protected Overrides Sub Render(writer As HtmlTextWriter)
writer.RenderBeginTag(HtmlTextWriterTag.Span)
If Not (ClassName Is Nothing) Then
writer.AddAttribute(HtmlTextWriterAttribute.Class, ClassName)
End If
writer.Write([Text])
writer.RenderEndTag()
End Sub
End Class
Now, we will look at the demonstration with our custom label inheriting from the WebControl class:
// [C#]
[ParseChildren(false)]
[PersistChildren(false)]
[ToolboxData("<{0}:CustomLabel runat=server></{0}:CustomLabel>")]
public class CustomLabel : WebControl {
private string pText = String.Empty;
public string Text {
get { return pText; }
set { pText = value; }
}
protected override HtmlTextWriterTag TagKey {
get { return HtmlTextWriterTag.Span; }
}
protected override void OnInit(EventArgs e) {
if ((Controls.Count > 0) && (Controls[0] is LiteralControl))
Text = ((LiteralControl)Controls[0]).Text;
base.OnInit (e);
}
protected override void RenderContents(HtmlTextWriter writer) {
writer.Write(Text);
}
}
' [VB.NET]
<ParseChildren(False), PersistChildren(False)> _
<ToolboxData("<{0}:CustomLabel runat=server></{0}:CustomLabel>")> _
Public Class CustomLabel
Inherits WebControl
Private pText As String = [String].Empty
Public Property [Text]() As String
Get
Return pText
End Get
Set (value As String)
pText = value
End Set
End Property
Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag
Get
Return HtmlTextWriterTag.Span
End Get
End Property
Protected Overrides Sub OnInit(e As EventArgs)
If Controls.Count > 0 AndAlso TypeOf Controls(0) Is LiteralControl Then
[Text] = CType(Controls(0), LiteralControl).Text
End If
MyBase.OnInit(e)
End Sub
Protected Overrides Sub RenderContents(writer As HtmlTextWriter)
writer.Write([Text])
End Sub
End Class
Both controls pass on the same minimal functionality; however, only one of them can walk away with an “A” to their name. That “A” will be awarded to the one that inherits from the WebControl class. For the following reason – it is able to be extended very easily by both a page developer and a control developer. Some bright control developer might want to make the tag that contains the text output to be a <div> instead of a <span> tag. Some page developer might want to add some CSS style for the label's style.
For our label control that inherits from the Control class to have that same functionality as the label control that inherits from the WebControl class, a ton of additional lines needs to be added. Another consideration when deciding which one to pursue, also think of making the lives of other control developers (including yourself) to be made easier – read further into the article to learn more about it.