Creating Custom Web Controls


Visual Basic .NET Unleashed
By Paul Kimmel
Table of Contents
Chapter 19.  ASP.NET Web Programming

Creating Custom Web Controls

There are a few basic ways to create custom controls for ASP.NET Web applications. Essentially, creating controls for the Web involves similar processes to creating controls for Windows Forms. You can create a control derived from a UserControl or create a Web Control Library project and generalize an existing Web Server Control.

There is a third way to create user controls that involves removing code more than writing code. This third way is to use the design view to visually design a Web page, add the code to the page, and then save the page as a server control.

We'll cover these three ways of creating user controls and custom controls, as well as demonstrating a couple of interesting solutions using these controls.

Saving a Web Page as a Server Control

A server control created by chopping out some HTML tags is easy to create and has some attractors and detractors.

If you want to create a server control from a Web page, you have all of the benefits of the Visual Studio Designer when creating the initial page. Hence, you can drag and drop controls onto the page and add the code behind the form. Unfortunately, when you are prepared to use the control you have, there is limited support for manipulating the page-with-control after it has been converted to a control.

The result is that controls contrived from pages are relatively easier to design and create but are most useful for specific solutions within a single application. (Don't worry, you can create custom controls that are very portable with other techniques. We'll get to those in a moment.)

To create a control from a Web page, you will need to design the page and follow these numbered steps:

  1. After you have the Web page designed, you need to remove all of the beginning and ending <HTML>, <FORM>, <BODY>, and <HEAD> tags from the HTML source.

  2. The raw HTML contains an @ Page directive. Change the @ Page directive to an @ Control directive, and get rid of attributes that are not supported by the @ Control directive. (Look in the help documentation for supported attributes.)

  3. Change the extension of the .aspx file to .ascx and the .aspx.vb (code-behind) file to .ascx.vb.

  4. Change the extension of the code-behind attribute in the Raw HTML file to reflect the change to the code-behind file extension.

  5. Change the Inherits statement in the code-behind from Inherits System.Web.UI.Page to Inherits System.Web.UI.UserControl.

After you have saved the new .ascx (control files), you can drag and drop the .ascx file from the Solution Explorer to a new Web page, to use the control.

The next two listings show the before and after changes to the raw HTML text. The first, Listing 19.12, shows the HTML (in the .aspx file); the page contains a DropDownList, a Button, and a TextBox. The second, Listing 19.13, shows the .ascx file with all of the necessary revisions.

Listing 19.12 A Web page before it is converted to a control
[View full width]
  1:  <%@ Page Language="vb" AutoEventWireup="false"  2:  Codebehind="BeforeAndAfter.aspx.vb"  3:  Inherits="SearchControl.BeforeAndAfter"%>  4:   5:  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  6:  <HTML>  7:  <HEAD>  8:  <title>BeforeAndAfter</title>  9:  <meta name="GENERATOR"  10:  content="Microsoft Visual Studio.NET 7.0">  11:   12:  <meta name="CODE_LANGUAGE"  13:  content="Visual Basic 7.0">  14:   15:  <meta name="vs_defaultClientScript"  16:  content="JavaScript">  17:   18:  <meta name="vs_targetSchema" content=" graphics/ccc.gif ie5">  19:   20:  </HEAD>  21:  <body MS_POSITIONING="GridLayout">  22:  <form id="Form1" method="post" runat="server">  23:   24:  <asp:DropDownList id="DropDownList1"  25:  style="Z-INDEX: 101; LEFT: 23px;  26:  POSITION: absolute; TOP: 22px"  27:  runat="server" Width="376px" Height="29px">  28:  </asp:DropDownList>  29:   30:  <asp:Button id="Button1" style="Z-INDEX: 102;  31:  LEFT: 413px; POSITION: absolute;  32:  TOP: 26px" runat="server" Width="39px"  33:  Height="27px" Text="Button">  34:  </asp:Button>  35:   36:  <asp:TextBox id="TextBox1" style="Z-INDEX: 103;  37:  LEFT: 29px; POSITION: absolute; TOP: 64px"  38:  runat="server" Width="610px" Height="345px"  39:  TextMode="MultiLine">  40:  </asp:TextBox>  41:  </form>  42:  </body>  43:  </HTML> 

I will point out the elements we need to modify after you have a chance to look at the revised page converted to a control.

Listing 19.13 The code from Listing 19.12 converted from a page to a control
  1:  <%@ Control Language="vb" AutoEventWireup="false"  2:  Codebehind="BeforeAndAfter.ascx.vb"  3:  Inherits="SearchControl.BeforeAndAfter"%>  4:  <asp:DropDownList id="DropDownList1"  5:  style="Z-INDEX: 101; LEFT: 23px;  6:  POSITION: absolute; TOP: 22px"  7:  runat="server" Width="376px" Height="29px">  8:  </asp:DropDownList>  9:  <asp:Button id="Button1" style="Z-INDEX: 102;  10:  LEFT: 413px; POSITION: absolute;  11:  TOP: 26px" runat="server" Width="39px"  12:  Height="27px" Text="Button">  13:  </asp:Button>  14:  <asp:TextBox id="TextBox1" style="Z-INDEX: 103;  15:  LEFT: 29px; POSITION: absolute; TOP: 64px"  16:  runat="server" Width="610px" Height="345px"  17:  TextMode="MultiLine">  18:  </asp:TextBox> 

Comparing Listing 19.12 to Listing 19.13, you can determine that lines 4 through 23 were removed. Lines 41, 42, and 43 were also removed. This takes care of step 1. Step 2 indicates that we have to change the @ Page directive. This directive is on line 1. Change Page to Control leaving the space between the At (@) and the directive word. The attributes used for the Page directive apply to the Control directive, so we will leave those in place.

Step 3 indicates that we need to change the .aspx and .aspx.vb extensions. If you simply rename the .aspx file extension to .ascx in the Solution Explorer, Visual Studio will change the .aspx.vb file to .ascx.vb and modify the code-behind directive too (see line 2). This takes care of step 4 automatically.

The fifth and final step indicates to change the inheritance from Page to UserControl. Just edit the code. You will get a runtime exception reminding you that @ Control classes must inherit from System.Web.UI.UserControl if you forget.

Adding the User Control to a Web Page

Using a user control is straightforward. Drag the control from the Solution Explorer to the Web page. If you ever have to perform this step manually, here is what Visual Studio .NET does for you.

When you drag a user control from the Solution Explorer to a Web Form, Visual Studio .NET adds an @ Register directive to the HTML code and includes the HTML that describes the control instance.

 <%@ Register tagPrefix="uc1" tagName="BeforeAndAfter" src="BeforeAndAfter.ascx" %> 

The register directive associates aliases with namespaces and classes. The tagPrefix attribute is to associate an alias with a namespace. The tagName is an alias to associate with a class. The src is the location of the user control. The assembly attribute (not shown) is the name of the assembly containing the user control. The nameSpace (not shown) is the namespace associated with the tagPrefix.

When you drag the control onto a Web page, the HTML describing the control is added to the form. The HTML is started with a beginning tag in the form tagprefix : tagname and ends with the </ tagprefix : tagname > tag. Between the beginning and ending tag are the attributes to initialize the control.

Figure 19.6 shows a search user control specifically designed to search order information in the Northwind.mdb database. The user control HTML is provided in Listing 19.14 and the Visual Basic .NET code is provided in Listing 19.15.

Figure 19.6. A user control that searches a database using a stored procedure.


Listing 19.14 HTML code for the SearchControl user control
[View full width]
 <%@ Control Language="vb" AutoEventWireup="false" Codebehind="SearchForm.ascx.vb" Inherits="SearchControl.SearchControl"%> <P>   <asp:DropDownList id="DropDownList1" runat="server" Width="323px" Height="265px"></asp: graphics/ccc.gif DropDownList>   <asp:Button id="Button1" runat="server" Width="37px" Text="Go"></asp:Button> </P> <P>   <asp:DataGrid id="DataGrid1" Height="424px" Width="622px" runat="server"></asp:DataGrid> </P> 
Listing 19.15 Visual Basic .NET code that implements the user control
  1:  Imports System.Data.SqlClient  2:   3:   4:  Public Class SearchControl  5:  Inherits System.Web.UI.UserControl  6:  Protected WithEvents DropDownList1 As System.Web.UI.WebControls.DropDownList  7:  Protected WithEvents TextBox1 As System.Web.UI.WebControls.TextBox  8:  Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid  9:  Protected WithEvents SqlConnection1 As System.Data.SqlClient.SqlConnection  10:  Protected WithEvents Adapter As System.Data.SqlClient.SqlDataAdapter  11:  Protected WithEvents SqlSelectCommand1 As System.Data.SqlClient.SqlCommand  12:  Protected WithEvents SqlInsertCommand1 As System.Data.SqlClient.SqlCommand  13:  Protected WithEvents SqlUpdateCommand1 As System.Data.SqlClient.SqlCommand  14:  Protected WithEvents SqlDeleteCommand1 As System.Data.SqlClient.SqlCommand  15:  Protected WithEvents Button1 As System.Web.UI.WebControls.Button  16:   17:  [ Web Form Designer Generated Code ]  18:   19:  Private Sub Page_Load(ByVal sender As System.Object, _  20:  ByVal e As System.EventArgs) Handles MyBase.Load  21:  'Put user code to initialize the page here  22:   23:  If (IsPostBack) Then Exit Sub  24:  Initialize()  25:   26:  End Sub  27:   28:  Private Sub Initialize()  29:   30:  SqlConnection1.Open()  31:  Dim Table As New DataSet()  32:  Adapter.Fill(Table)  33:   34:  DropDownList1.DataSource = Table  35:  DropDownList1.DataTextField = "CompanyName"  36:  DropDownList1.DataValueField = "CustomerID"  37:  DropDownList1.DataBind()  38:   39:  SqlConnection1.Close()  40:   41:  End Sub  42:   43:  Private Function GetQuery(ByVal Key As String)  44:  Const SQL As String = _  45:  "SELECT * FROM Orders O, [Order Details] D " & _  46:  "WHERE O.OrderID = D.OrderID " & _  47:  "AND CustomerID = '{0} '"  48:   49:  Return String.Format(SQL, Key)  50:  End Function  51:   52:  Private Sub ShowOrders(ByVal Key As String)  53:   54:  Dim Command As New SqlCommand("CustOrderHist", SqlConnection1)  55:  Command.CommandType = CommandType.StoredProcedure  56:  Command.Parameters.Add(New SqlParameter("@CustomerID", Key))  57:   58:  SqlConnection1.Open()  59:  Dim Reader As SqlDataReader = _  60:  Command.ExecuteReader(CommandBehavior.Default)  61:   62:  DataGrid1.DataSource = Reader  63:  DataGrid1.DataBind()  64:   65:  SqlConnection1.Close()  66:  End Sub  67:   68:  Private Sub DropDownList1_SelectedIndexChanged(_  69:  ByVal sender As System.Object,  ByVal e As System.EventArgs) _  70:  Handles DropDownList1.SelectedIndexChanged  71:   72:  ShowOrders(CType(sender, DropDownList).SelectedItem.Value)  73:   74:  End Sub  75:  End Class 

Most of this code you have seen earlier in this chapter. There are a couple of revisions. A SqlDataAdapater and SqlConnection were used and configured automatically by dragging the Customers table from the Server window (see Figure 19.7) to the component tray, and ShowOrders demonstrates the revisions necessary to call a stored procedure. The stored procedure performs roughly the same task as the SQL returned by the GetQuery method beginning on line 43.

Figure 19.7. Drag and drop elements from the Server window for automatic database connectivity.



Use stored procedures when possible for optimal performance and application scalability.

Line 54 initializes the SqlCommand object with the name of a pre-existing stored procedure in the Northwind database. Line 55 changes the command type to CommandType.StoredProcedure, and line 56 creates and passes the SqlParameter object to the SqlCommand object before requesting the SQlDataReader.

Creating the User Control Programmatically

To create a user control programmatically (see Listing 19.16), you will need to include the @ Register directive at design time. After the user control has been registered in your application, you can create an instance of it by calling the Page.LoadControl method.

Listing 19.16 Dynamically loading a user control
  1:  Private Sub Page_Load(ByVal sender As System.Object, _  2:  ByVal e As System.EventArgs) Handles MyBase.Load  3:  'Put user code to initialize the page here  4:   5:  Dim Control As UserControl = LoadControl("SearchForm.ascx")  6:  Dim I As Integer  7:  For I = 0 To Controls.Count - 1  8:  If (Controls(I).GetType() Is GetType(HtmlForm)) Then  9:  CType(Controls(I), HtmlForm).Controls.Add(Control)  10:  End If  11:  Next  12:   13:  End Sub 

Line 5 demonstrates how to load the user control. Declare a UserControl reference and initialize it with a call to LoadControl, passing the name of the .ascx file containing the control. Add the control to a container control. (Recall that you can use a PlaceHolder or Panel, which is easier than finding the HtmlForm object.)


You can rewrite line 5 as Dim Control As New SearchControl, although this results in some uninitialized data and an object reference error on line 97 of SearchForm.ascx.vb. This may be a bug in ASP.NET. LoadControl seems to work reliably for dynamic user control creation.

Creating a Custom User Control

The fastest way to create a user control is to choose Project, Add Web User Control in Visual Studio .NET. Similar to building user controls for Windows Forms, this approach will provide you with RAD design-time support and coding.

Add code and controls to the UserControl just as you would to a Web Form, and you can use the control by dragging it onto a form or loading it dynamically (see the previous section).

Creating a Web Control Library

Custom, or composite, controls are moderately more difficult to create but offer a different set of advantages than creating user controls. Table 19.2 compares the advantages and costs of creating composite controls in a Web Control Library to those of creating user controls. Consider your objectives before deciding which kind of control to create.

Table 19.2. A comparison between composite and user controls
Composite Control User Control
Minimum design-time Full, RAD design-time support support
Creating by writing code Creating by dragging and dropping controls and adding code-behind modules or using script
Compiled into a .DLL assembly Persisted as an .ascx text file with optional code-behind file, .ascx.vb
Good for creating redistributable controls Good for specific application solutions
Complete design-time control support and can be added to the toolbox Very little design-time support when using the user control

The best reason to build a composite control in a Web Control Library is that you want to build a redistributable, generic control bundled in a tidy .DLL assembly. If you just need a control that will be reused a couple of times in the same application, a user control is easier to create.

The general steps for creating a Web Control Library follow:

  1. Create a Web Control Library Project. This will create a Web Control project with a generic class inheriting from System.Web.UI.WebControl. (The control will have a default text property.)

  2. Modify the inheritance statement to inherit from a subclass of WebControl that has the closest behavior to the control you want to create. (To create a brand-new control, inherit from WebControl. To extend an existing WebControl, inherit from the existing control.)

  3. Define properties and methods for the control.

  4. Override the Render method to define the presentation of the control. (Refer to Listing 19.17 for the module code created by the Web Control Library template.)

Listing 19.17 Contains the default code generated by the Web Control Library template
  1:  Imports System.ComponentModel  2:  Imports System.Web.UI  3:   4:  <DefaultProperty("Text"), ToolboxData("<{0} :WebCustomControl1 runat=server> _  5:  </{0} :WebCustomControl1>")> Public Class WebCustomControl1  6:  Inherits System.Web.UI.WebControls.WebControl  7:   8:  Dim _text As String  9:   10:  <Bindable(True), Category("Appearance"), DefaultValue("")> _  11:  Property [Text]() As String  12:  Get  13:  Return _text  14:  End Get  15:   16:  Set(ByVal Value As String)  17:  _text = Value  18:  End Set  19:  End Property  20:   21:  Protected Overrides Sub Render(_  22:  ByVal output As System.Web.UI.HtmlTextWriter)  23:  output.Write([Text])  24:  End Sub  25:   26:  End Class 

After 19 chapters, I'm tempted to say you know what all of these elements are and how to use them. In case you skipped ahead to this chapter, I will review this code because it looks a little strange with all of the attribute information.

If you look at lines 5 and 26, you will see that this is just a class. If you look at lines 11 and 19, you will see that Text is just a property.

The DefaultProperty attribute comes from the ComponentModel namespace and tells the compiler which property is the default property.

Normally the HTML tag for a control is an empty tag. The ToolboxData attribute allows you to specify attributes for the designer-generated HTML tag representing the control. The ToolboxDataAttribute on line 4 provides a format string for the tag and includes the runat="server" attribute, indicating this control is a server control.

The BindableAttribute indicates whether a particular property is bindable at design time. The CategoryAttribute is used to categorize the property in the Properties window, and DefaultValueAttribute enables you to indicate a default value for the property.

In the example, this control is rendered as the value of its text property.


Visual BasicR. NET Unleashed
Visual BasicR. NET Unleashed
Year: 2001
Pages: 222 © 2008-2017.
If you may any questions please contact us: