http://www.beansoftware.com/ASP.NET-Tutorials/Custom-Server-Controls.aspx
In ASP.NET is possible to build two types of controls. That are custom server controls and user controls, which are pretty different. More about creating and using of user controls you can find in User Control In ASP.NET tutorial. This article will cover creating of custom server controls.
Unlike user controls, custom server controls are not placed in /App_Code folder but compiled to dll. You need to write complete control in code and there is no some visual designer (no markup code and no .ascx file). Because of that, custom controls are harder to create, but when created and compiled, they are easier to use. You can build installation package for custom controls and distribute them easy.
If you use Visual Web Developer Express you'll see that there is no ASP.NET Server Control template, but you can download Visual C# Express or Visual Basic Express and create Class Library project. You can create your custom controls in Notepad too, just name text file with extension of .vb or .cs and write class that inherits System.UI.WebControls.WebControl, like in code bellow:
[ C# ]
public class ServerControl1 : WebControl
{
}
[ VB.NET ]
Public Class ServerControl1
Inherits WebControl
End Class
In case that you don't want to build control from scratch, but only to add new feature to existing control, you can inherit that control directly instead of WebControl class and write additional code. All standard controls from Visual Studio toolbox are also inherited from WebControl or from a Control class. WebControl inherits Control class, so correct statement is that custom controls always intherit from System.Web.UI.Control class, directly or indirectly. You can check this in Object Browser (in Visual Studio menu go to View -> Object Browser or use Ctrl + Alt + J keyboard shortcut). It is better to use WebControl if your control will have visual interface. If there is no UI, inherit Control class.
Simple control written in code above can't do anything, but it is functional and it is possible to compile it to .dll file through Visual Studio IDE or command prompt.
[ C# ]
public string Text
{
get
{
// Read value from ViewState
String s = (String)ViewState["Text"];
// If ViewState is null return default value
return ((s == null) ? "Some Default Value, could be local constant" : s);
}
set
{
// Save value to ViewState
ViewState["Text"] = value;
}
}
[ VB.NET ]
Property Text() As String
Get
' Read value from ViewState
Dim s As String = CStr(ViewState("Text"))
' If ViewState is null return default value
If s Is Nothing Then
Return "Some Default Value, could be local constant"
Else
Return s
End If
End Get
Set(ByVal Value As String)
' Save value to ViewState
ViewState("Text") = Value
End Set
End Property
Web control uses ViewState to remember values between post backs. ViewState works nice, but there is a problem because it could be disabled on web page level. In some scenarios, property value is critical and control must get it to work properly. ASP.NET have ControlState which is not affected if ViewState is turned off. You should avoid using of ControlState if possible and use it only when necessary. More about how to use ControlState you can read on ControlState Property Demystified tutorial.
Adding methods for custom server controls is the same as for the other types of .Net classes. You can build your own method or override existing. Just take care if you need to keep functionality of base method, to execute method of base class, use base keyword in C# or MyBase keyword in VB.NET.
First, your custom server control's dll must be in web application /bin folder, or installed in GAC (Global Assembly Cache).
After that, you can add custom controls to web page in two simple steps:
First, you need to register custom controls at the top of the markup code (.aspx file) by using Register directive. You can do this with markup code like this:
<%@ Page Language="C#" AutoEventWireup="true"Â CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Namespace="BeanSoftware" Assembly="MyAssembly" TagPrefix="cc" %>
Second, on the place where you want to add custom control, use TagPrefix property value and class name of your custom control to build a tag, like this
<cc:MyClassName runat="server" ID="MyControl1" />
So, tag is built in this form TagPrefix:CustomControlClassName. Except this, you should add at least runat="server" and specify ID of control. In this example TagPrefix = "cc". There is no big reason for this, it is simply abbreviation for Custom Control. You can put practically anything here.
If the same server control will be used on many web pages, then you can register it in <controls> section in web.config:
<configuration>
<system.web>
<pages>
<controls>
<add namespace="MyNameSpaceName.SubNameSpace.MyControlClassName" tagPrefix="bs" assembly="MyAssembly" />
</controls >
</pages >
</system.web>
</configuration>
When registered in web.config, you don't need to write Register directives on every web page.
It is possible to build three types of web custom server controls in ASP.NET:
- Derived custom controls
- Composite controls
- Custom rendered controls
[ C# ]
public class ServerControl1 : TextBox
{
}
[ VB.NET ]
Public Class ServerControl1
Inherits TextBox
End Class
Control created on this way will have all functionality of based control. In this example, we'll get a complete TextBox control and now we need to add just code for additional features. It is possible to create new properties, methods and events or to override existing.
This sounds complicated, but let see how it looks on simple example. We'll build composite control of one TextBox named "txtName", one Button contro "btnSayHi", and one Label "lblMessage". User will type her name in txtName and on button's click event we'll show welcoming message in label control.
[ C# ]
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace BeanSoftware
{
[ToolboxData("<{0}:WelcomeCompositeControl runat=server></{0}:WelcomeCompositeControl>")]
public class WelcomeCompositeControl : System.Web.UI.WebControls.WebControl, INamingContainer
{
// Declare subcontrols
protected TextBox txtName = new TextBox();
protected Label lblMessage = new Label();
protected Button btnHi = new Button();
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return base.Controls;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
// Set subcontrols behavior
txtName.ToolTip = "Write your name here and click a button.";
lblMessage.ForeColor = System.Drawing.Color.Red;
btnHi.Text = "Say Hi!";
btnHi.Click +=new EventHandler(btnHi_Click);
// Create user interface by using Controls collection
Controls.Add(txtName);
Controls.Add(new LiteralControl("<br />"));
Controls.Add(lblMessage);
Controls.Add(new LiteralControl("<br />"));
Controls.Add(btnHi);
}
// Handles btnHi button click event
private void btnHi_Click(object sender, EventArgs e)
{
// Call this method first whenever do something
// with subcontrols
EnsureChildControls();
// Show welcome message in label
lblMessage.Text = "Hi " + txtName.Text;
}
}
}
[ VB.NET ]
Imports System
Imports System.ComponentModel
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace BeanSoftware
<ToolboxData("<{0}:WelcomeCompositeControl runat=server></{0}:WelcomeCompositeControl>")> _
Public Class WelcomeCompositeControl
Inherits TextBox
Implements INamingContainer
' Declare subcontrols
Protected txtName As TextBox = New TextBox()
Protected lblMessage As Label = New Label()
Protected WithEvents btnHi As Button = New Button()
Public Overrides ReadOnly Property Controls() As ControlCollection
Get
EnsureChildControls()
Return MyBase.Controls
End Get
End Property
Protected Overrides Sub CreateChildControls()
Controls.Clear()
' Set subcontrols behavior
txtName.ToolTip = "Write your name here and click a button."
lblMessage.ForeColor = System.Drawing.Color.Red
btnHi.Text = "Say Hi!"
' Create user interface by using Controls collection
Controls.Add(txtName)
Controls.Add(New LiteralControl("<br />"))
Controls.Add(lblMessage)
Controls.Add(New LiteralControl("<br />"))
Controls.Add(btnHi)
End Sub
' Handles btnHi button click event
Private Sub btnHi_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnHi.Click
' Call this method first whenever do something
' with subcontrols
EnsureChildControls()
' Show welcome message in label
lblMessage.Text = "Hi " & txtName.Text
End Sub
End Class
End Namespace
[ C# ]
protected override void RenderContents(HtmlTextWriter output)
{
output.AddStyleAttribute(HtmlTextWriterStyle.Color, "red");
output.RenderBeginTag(HtmlTextWriterTag.Span);
output.Write("This text will be red");
output.RenderEndTag();
}
[ VB.NET ]
Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)
output.AddStyleAttribute(HtmlTextWriterStyle.Color, "red")
output.RenderBeginTag(HtmlTextWriterTag.Span)
output.Write("This text will be red")
output.RenderEndTag()
End Sub
As you see, nothing visual here, you need to write every attribute and every tag manually. Also, since you have complete control, you need to pay attention to cross browser comaptibility. All this could be very confusing in complex projects. More about custom rendered controls you can find at Custom Rendered Controls In ASP.NET tutorial.
[ C# ]
protected override void OnInit(EventArgs e)
{
// Check first if script is already registered to
// avoid multiple registrations of the same client code
if (!Page.IsClientScriptBlockRegistered("MyClientScript"))
{
// client code to be registered
string JavaScriptCode = @"<script>
function SomeJavaScriptFunction()
{
// JavaScript code goes here
}
</script>";
// Registering JavaScript code
Page.RegisterClientScriptBlock("MyClientScript",
JavaScriptCode);
}
// Keep functionality of base method
base.OnInit(e);
}
[ VB.NET ]
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
' Check first if script is already registered to
' avoid multiple registrations of the same client code
If Not Page.IsClientScriptBlockRegistered("MyClientScript") Then
' client code to be registered
Dim JavaScriptCode As String = "<script>" & vbCrLf & _
"function SomeJavaScriptFunction()" & vbCrLf & _
"{" & vbCrLf & _
"// JavaScript code goes here" & vbCrLf & _
"}" & vbCrLf & _
"</script>"
' Registering JavaScript code
Page.RegisterClientScriptBlock("MyClientScript", _
JavaScriptCode)
End If
' Keep functionality of base method
MyBase.OnInit(e)
End Sub
On this way, client side block will be registered only for first instance of custom server control on web form. For every next instance, checking of Page.IsClientScriptBlockRegistered will prevent repeated registration. Be aware that not every web browser supports JavaScript, especially those on mobile devices. Even if browser supporting JavaScript, some visitors could block it manually by using security settings of web browser.
To see how these examples are made check ASP.NET Media Player Control and ASP.NET JW FLV Flash Video Player Control. Both controls are free and include source code in C# and VB.NET.
Custom Server Controls In ASP.NET
When developer starts to learn ASP.NET, he or she plays with controls from the Visual Studio toolbox and build different user interfaces. Sooner or later, developer wants to encapsulate parts of web page to user controls, extend standard controls to add new features or even build custom web controls or components from scratch. On this way developer increases reusability, reduce repetitive code and ease maintenance.Unlike user controls, custom server controls are not placed in /App_Code folder but compiled to dll. You need to write complete control in code and there is no some visual designer (no markup code and no .ascx file). Because of that, custom controls are harder to create, but when created and compiled, they are easier to use. You can build installation package for custom controls and distribute them easy.
How to create web custom controls
In Visual Studio, create a new project. Choose the "ASP.NET Server Control" type, or "Web Custom Control" type if you use earlier version of Visual Studio. In any case, Visual Studio will create a class that inherits from WebControl class, which is located in System.Web.UI.WebControls interface.If you use Visual Web Developer Express you'll see that there is no ASP.NET Server Control template, but you can download Visual C# Express or Visual Basic Express and create Class Library project. You can create your custom controls in Notepad too, just name text file with extension of .vb or .cs and write class that inherits System.UI.WebControls.WebControl, like in code bellow:
[ C# ]
public class ServerControl1 : WebControl
{
}
[ VB.NET ]
Public Class ServerControl1
Inherits WebControl
End Class
In case that you don't want to build control from scratch, but only to add new feature to existing control, you can inherit that control directly instead of WebControl class and write additional code. All standard controls from Visual Studio toolbox are also inherited from WebControl or from a Control class. WebControl inherits Control class, so correct statement is that custom controls always intherit from System.Web.UI.Control class, directly or indirectly. You can check this in Object Browser (in Visual Studio menu go to View -> Object Browser or use Ctrl + Alt + J keyboard shortcut). It is better to use WebControl if your control will have visual interface. If there is no UI, inherit Control class.
Simple control written in code above can't do anything, but it is functional and it is possible to compile it to .dll file through Visual Studio IDE or command prompt.
Adding properties and methods to custom server control
Adding properties to custom server controls is similar to adding properties to other types of classes in .Net framework. Only difference is the way how we keep property values. Because http is stateless protocol, all variables and class are destroyed after web request is finished. To persist property value between post backs, you can use ViewState. Example property that use ViewState to save its value will look like this:[ C# ]
public string Text
{
get
{
// Read value from ViewState
String s = (String)ViewState["Text"];
// If ViewState is null return default value
return ((s == null) ? "Some Default Value, could be local constant" : s);
}
set
{
// Save value to ViewState
ViewState["Text"] = value;
}
}
[ VB.NET ]
Property Text() As String
Get
' Read value from ViewState
Dim s As String = CStr(ViewState("Text"))
' If ViewState is null return default value
If s Is Nothing Then
Return "Some Default Value, could be local constant"
Else
Return s
End If
End Get
Set(ByVal Value As String)
' Save value to ViewState
ViewState("Text") = Value
End Set
End Property
Web control uses ViewState to remember values between post backs. ViewState works nice, but there is a problem because it could be disabled on web page level. In some scenarios, property value is critical and control must get it to work properly. ASP.NET have ControlState which is not affected if ViewState is turned off. You should avoid using of ControlState if possible and use it only when necessary. More about how to use ControlState you can read on ControlState Property Demystified tutorial.
Adding methods for custom server controls is the same as for the other types of .Net classes. You can build your own method or override existing. Just take care if you need to keep functionality of base method, to execute method of base class, use base keyword in C# or MyBase keyword in VB.NET.
How to place custom server control to web page
After that, you can add custom controls to web page in two simple steps:
First, you need to register custom controls at the top of the markup code (.aspx file) by using Register directive. You can do this with markup code like this:
<%@ Page Language="C#" AutoEventWireup="true"Â CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Namespace="BeanSoftware" Assembly="MyAssembly" TagPrefix="cc" %>
Second, on the place where you want to add custom control, use TagPrefix property value and class name of your custom control to build a tag, like this
<cc:MyClassName runat="server" ID="MyControl1" />
So, tag is built in this form TagPrefix:CustomControlClassName. Except this, you should add at least runat="server" and specify ID of control. In this example TagPrefix = "cc". There is no big reason for this, it is simply abbreviation for Custom Control. You can put practically anything here.
If the same server control will be used on many web pages, then you can register it in <controls> section in web.config:
<configuration>
<system.web>
<pages>
<controls>
<add namespace="MyNameSpaceName.SubNameSpace.MyControlClassName" tagPrefix="bs" assembly="MyAssembly" />
</controls >
</pages >
</system.web>
</configuration>
When registered in web.config, you don't need to write Register directives on every web page.
It is possible to build three types of web custom server controls in ASP.NET:
- Derived custom controls
- Composite controls
- Custom rendered controls
Derived custom server controls
If you want to extend existing control and add some new features, you can build derived custom control. When you create new server control, Visual Studio by default creates class that inherits from WebControl class. You can change this and inherit any other control. For example, to create server control inherited from TextBox, you should use code like this:[ C# ]
public class ServerControl1 : TextBox
{
}
[ VB.NET ]
Public Class ServerControl1
Inherits TextBox
End Class
Control created on this way will have all functionality of based control. In this example, we'll get a complete TextBox control and now we need to add just code for additional features. It is possible to create new properties, methods and events or to override existing.
Composite server controls
Composite control is a control that are built from other controls. To build composite control you need to inherit WebControl class and implement INamingContainer interface. This interface have not any implementation, but it is needed to subcontrols get unique names on web page. After that, override ControlCollection property and CreateChildControls method. In ControlCollection property just call EnsureChildControls() method. EnsureChildControls method checks if CreateChildControls is already called, and call it if not. In CreateChildControls method we define the look of composite control and subcontrols are added by using of Controls.Add method.This sounds complicated, but let see how it looks on simple example. We'll build composite control of one TextBox named "txtName", one Button contro "btnSayHi", and one Label "lblMessage". User will type her name in txtName and on button's click event we'll show welcoming message in label control.
[ C# ]
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace BeanSoftware
{
[ToolboxData("<{0}:WelcomeCompositeControl runat=server></{0}:WelcomeCompositeControl>")]
public class WelcomeCompositeControl : System.Web.UI.WebControls.WebControl, INamingContainer
{
// Declare subcontrols
protected TextBox txtName = new TextBox();
protected Label lblMessage = new Label();
protected Button btnHi = new Button();
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return base.Controls;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
// Set subcontrols behavior
txtName.ToolTip = "Write your name here and click a button.";
lblMessage.ForeColor = System.Drawing.Color.Red;
btnHi.Text = "Say Hi!";
btnHi.Click +=new EventHandler(btnHi_Click);
// Create user interface by using Controls collection
Controls.Add(txtName);
Controls.Add(new LiteralControl("<br />"));
Controls.Add(lblMessage);
Controls.Add(new LiteralControl("<br />"));
Controls.Add(btnHi);
}
// Handles btnHi button click event
private void btnHi_Click(object sender, EventArgs e)
{
// Call this method first whenever do something
// with subcontrols
EnsureChildControls();
// Show welcome message in label
lblMessage.Text = "Hi " + txtName.Text;
}
}
}
[ VB.NET ]
Imports System
Imports System.ComponentModel
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace BeanSoftware
<ToolboxData("<{0}:WelcomeCompositeControl runat=server></{0}:WelcomeCompositeControl>")> _
Public Class WelcomeCompositeControl
Inherits TextBox
Implements INamingContainer
' Declare subcontrols
Protected txtName As TextBox = New TextBox()
Protected lblMessage As Label = New Label()
Protected WithEvents btnHi As Button = New Button()
Public Overrides ReadOnly Property Controls() As ControlCollection
Get
EnsureChildControls()
Return MyBase.Controls
End Get
End Property
Protected Overrides Sub CreateChildControls()
Controls.Clear()
' Set subcontrols behavior
txtName.ToolTip = "Write your name here and click a button."
lblMessage.ForeColor = System.Drawing.Color.Red
btnHi.Text = "Say Hi!"
' Create user interface by using Controls collection
Controls.Add(txtName)
Controls.Add(New LiteralControl("<br />"))
Controls.Add(lblMessage)
Controls.Add(New LiteralControl("<br />"))
Controls.Add(btnHi)
End Sub
' Handles btnHi button click event
Private Sub btnHi_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnHi.Click
' Call this method first whenever do something
' with subcontrols
EnsureChildControls()
' Show welcome message in label
lblMessage.Text = "Hi " & txtName.Text
End Sub
End Class
End Namespace
Custom rendered server controls
In custom rendered server controls you need to build complete output. First, you need to override RenderContents method. Then, use HtmlTextWriter object to create control's UI. In some basic example let output of control be text colored in red, RenderContents method could look like this:[ C# ]
protected override void RenderContents(HtmlTextWriter output)
{
output.AddStyleAttribute(HtmlTextWriterStyle.Color, "red");
output.RenderBeginTag(HtmlTextWriterTag.Span);
output.Write("This text will be red");
output.RenderEndTag();
}
[ VB.NET ]
Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)
output.AddStyleAttribute(HtmlTextWriterStyle.Color, "red")
output.RenderBeginTag(HtmlTextWriterTag.Span)
output.Write("This text will be red")
output.RenderEndTag()
End Sub
As you see, nothing visual here, you need to write every attribute and every tag manually. Also, since you have complete control, you need to pay attention to cross browser comaptibility. All this could be very confusing in complex projects. More about custom rendered controls you can find at Custom Rendered Controls In ASP.NET tutorial.
Adding client javascript to custom server control
To enable client side functionality, often we need to use some JavaScript code. Although you can write this code by using HtmlTextWriter, it is not recommended because the same JavaScript would be vritten for every instance of control on the web page. Because of this, it is better to use Page.RegisterClientScriptBlock method. To get better perfomance, call this method in overriden OnInit method instead of using RenderContents. Client side script registration could look like this:[ C# ]
protected override void OnInit(EventArgs e)
{
// Check first if script is already registered to
// avoid multiple registrations of the same client code
if (!Page.IsClientScriptBlockRegistered("MyClientScript"))
{
// client code to be registered
string JavaScriptCode = @"<script>
function SomeJavaScriptFunction()
{
// JavaScript code goes here
}
</script>";
// Registering JavaScript code
Page.RegisterClientScriptBlock("MyClientScript",
JavaScriptCode);
}
// Keep functionality of base method
base.OnInit(e);
}
[ VB.NET ]
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
' Check first if script is already registered to
' avoid multiple registrations of the same client code
If Not Page.IsClientScriptBlockRegistered("MyClientScript") Then
' client code to be registered
Dim JavaScriptCode As String = "<script>" & vbCrLf & _
"function SomeJavaScriptFunction()" & vbCrLf & _
"{" & vbCrLf & _
"// JavaScript code goes here" & vbCrLf & _
"}" & vbCrLf & _
"</script>"
' Registering JavaScript code
Page.RegisterClientScriptBlock("MyClientScript", _
JavaScriptCode)
End If
' Keep functionality of base method
MyBase.OnInit(e)
End Sub
On this way, client side block will be registered only for first instance of custom server control on web form. For every next instance, checking of Page.IsClientScriptBlockRegistered will prevent repeated registration. Be aware that not every web browser supports JavaScript, especially those on mobile devices. Even if browser supporting JavaScript, some visitors could block it manually by using security settings of web browser.
Few custom server controls examples
Controls and components are on some way packaged knowledge. If you need to use the same functionality over and over again, you can place it to custom control instead of rewriting it. Let say you need to display different video file formats in web application. On client side, Flash Player and Windows Media Player objects are used. It is good idea to wrap client side logic to server side controls. Then, in web application you work with custom server controls that you can drag and drop from Visual Studio toolbox, easy manipulate with server side code etc.To see how these examples are made check ASP.NET Media Player Control and ASP.NET JW FLV Flash Video Player Control. Both controls are free and include source code in C# and VB.NET.