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 ControlA 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:
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="http://schemas.microsoft.com/intellisense/ 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 control1: <%@ 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 PageUsing 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: 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 control1: 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.
Tip 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 ProgrammaticallyTo 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 control1: 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.) Caution 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 ControlThe 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 LibraryCustom, 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
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:
Listing 19.17 Contains the default code generated by the Web Control Library template1: 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. |
Team-Fly |
Top |