User Controls


User Controls

In addition to using HTML and server controls in your ASP.NET page, you can also create custom server controls. There are four primary ways for creating these custom controls “ the easiest and simplest way will be covered in this chapter. A user control functions like an include file, but one with a defined interface. You can also take an existing control and derive a new control from it “ retaining the desired functionality, and modifying or adding new functionality. A composite control can be created by combining the functionality of two or more server (or user) controls into a new control. Finally, a custom server control can be created from scratch “ by starting with a base control class and extending it to support the functionality and the resulting HTML needed.

The idea behind user controls is that you can create reusable sections of code or content as separate ASP.NET controls, and then use them in other pages without having to change the code, or even be aware of how it works! And while code-behind techniques are primarily aimed at just inheriting code classes into a page, user controls can inherit parts of the user interface as well. In effect, they are a way of encapsulating other controls and code into a reusable package.

The programming model for writing user controls is exactly the same as that for writing ASP.NET pages “ if you know how to write ASP.NET pages, then you know how to write user controls. Because they are so easy to create, user controls enable a RAD-style development model for building reusable controls.

Approaches to Building User Control

There are two approaches that you can use to build a user control. You can take an existing ASP.NET page and convert it to a user control. Or, you can set out to build a user control from scratch. The advantage to creating one from an existing (and assumed working) page is that you can test it directly as an ASP.NET page, make sure it works, and then convert it to a user control. However, to do this, let's first create a user control from scratch.

Creating a User Control

At this point in the book, you already have everything you need to create user controls from scratch. A user control is nearly identical to a Web Forms page, except for a few minor differences. A user control does not have a <HTML> tag, a <BODY> tag, or even a <FORM> tag. Since a user control will be inserted into another page, the other page will already have these elements. And since a page can only have one set of these elements, it is important that they are not part of the user control.

A user control can contain client script, HTML elements, ASP.NET code, and other server controls. The simplest user control would be one that simply outputs HTML when the page is rendered:

  <hr>   <table border="0">   <tr><td colspan="2" align="center">This table is in a User Control</td></tr>   <tr><td align="right">Copyright: </td><td>2002 Wrox Press</td><tr>   <tr><td align="right">Page Created on: </td><td><%= Now() %></td></tr>   </table>  

Create this file on the server and save it as standardFooter.ascx . This is now a user control. User controls must have a file suffix of .ascx .

To test a user control, you need to add it to a Web Forms page. There are two steps to doing this. First, use the @ Register directive to tell the page that there is a new user control that will be used in the page:

  <%@ Register tagprefix="wrox" Tagname="footer" src="standardFooter.ascx" %>  

Next , add it to the page at the location where you want this information to be displayed:

  <wrox:footer runat="server" />  

The browser will display the page shown in Figure 4-8:

click to expand
Figure 4-8:

Converting a Page to a User Control

It is easy to create a user control from scratch. Now, let's look at the more common way of creating a user control. When developing ASP.NET web applications, there will be sections of code used on multiple pages. In the past, the options were to create an include file that kept the code in a single location, or maybe create a design-time control for Visual Studio. But neither of these methods was really that simple or straightforward to use. And all developers know that if something isn't simple and straightforward, then chances are it won't be used. But now with user controls, that code segment from our existing page can be taken and turned into a control that can be easily reused in multiple pages.

The example shown in the beginning of the chapter enabled you to select an item from a list of shipping methods. This page included the database code to retrieve the list as well as the controls to display the list. By packaging that logic into a reusable control, you can ensure that wherever a list of shippers is needed in your application (or applications) the data will be retrieved and displayed in the same way. A similar technique is used in Chapter 8 to build a control that returns data.

The first part of the control will be the display. The selection list is a drop-down list server control. What to do with the data in the control is up to the page that is hosting the control. Since the server control is using data binding, the code that makes up the display will be quite simple:

  <asp:DropDownList id="ShipMethod" runat="server"/>  

The main work of the page is done in the code that sets up the page. Since in effect the user control is a page, the code to read from the database in the Page_Load event can be the same as was used in the original page. Also, check to see if the page has a @Page directive. If it does, then change this to a @Control directive:

  <%@ Control Language="Visual Basic" %>     <script language="VB" runat="server">   Sub Page_Load(Source As Object, E As EventArgs)   If Not Page.IsPostBack Then     Dim myConnection As SqlConnection   Dim myCommand    As SqlCommand   Dim myReader     As SqlDataReader   Dim SQL          As String   Dim ConnStr      As String     SQL = "SELECT * FROM Shippers"   ConnStr = "server=localhost;uid=sa;pwd=;database=Northwind"     myConnection = New SqlConnection(ConnStr)   myConnection.Open()     myCommand = New SqlCommand(SQL, myConnection)     myReader = myCommand.ExecuteReader()     ShipMethod.DataSource = myReader   ShipMethod.DataBind()     End If     End Sub     </script>  

This is exactly the same code that you had in your ASP.NET page. Since there are references to other .NET assemblies in the code, add an @Import directive to the user control. Don't rely on the page hosting this control to have imported these references. It doesn't matter to the compiler if these references are imported twice “ but it will complain if they aren't there at all.

  <%@ Import Namespace="System.Data" %>   <%@ Import Namespace="System.Data.SqlClient" %>  

When viewing a page that contains this control, the browser will show the result (Figure 4-9):

click to expand
Figure 4-9:

On examining the HTML that this produces, you can see that nothing special is happening on the client:

  <html>   <head>   <title>Testing our User Control  Part 2</title>   </head>     <body>   <form name="ctrl0" method="post" action="UserControl2.aspx" id="ctrl0">   <input type="hidden" name="__VIEWSTATE"   value="dDwzNzA5NjI5Njk7dDw7bDxpPDE+Oz47bDx0PDtsPGk8MT47PjtsPHQ8O2w8aTwwPjs+O2w8dDx0   PDtwPGw8aTwwPjtpPDE+O2k8Mj47PjtsPHA8U3BlZWR5IEV4cHJlc3M7MT47cDxVbml0ZWQgUGFja2FnZTs   yPjtwPEZlZGVyYWwgU2hpcHBpbmc7Mz47Pj47Pjs7Pjs+Pjs+Pjs+Pjs+QCtfh/cNp+z/JarjDm/wQX8mmt   A=" />   <strong>Here is some body text.</strong>   <P>Please choose a shipping method:   <select name="ctrl1:ShipMethod" id="ctrl1_ShipMethod">   <option value="1">Speedy Express</option>   <option value="2">United Package</option>   <option value="3">Federal Shipping</option>   </select>   </P>   <hr>   <table border=0 align=center>   <tr><td colspan=2 align=center>This table is in a User Control</td></tr>   <tr><td align=right>Copyright: </td><td>2002 Wrox Press</td><tr>   <tr><td align=right>Page Created on: </td><td>22/01/2002 15:48:09</td></tr>   </table>   </form>   </body>   </html>  

The user control behaves just like any other control. The Page_Load runs and any content is transmitted to the parent page. The difference is that this user control can be dropped onto other pages.

The @ Control directive is used to assign control-specific attributes that are used by the Web Forms page parser and compiler to affect how the user control is created. There can only be one @ Control directive in a single file. The attributes are as follows :

Attribute

Values (default in bold)

Used for

AutoEventWireup

True or False

Indicates whether the page's events are automatically enabled

ClassName

Valid class name

Class name that this control is compiled as.

CompilerOptions

Valid compiler options

List of compiler options to be used when the page is compiled.

Debug

True or False

Compiles the page with debugging enabled.

Description

n/a

Description of the page “ ignored by ASP.NET.

EnableViewState

True or False

ViewState for this user control is maintained during round-trips.

Explicit

True or False

Uses the Visual Basic Option Explicit mode.

Inherits

Valid class name

Code-behind class that this control inherits.

Language

Valid .NET Language name

Language used to compile all sourcecode on the page.

Src

Valid source file name

File name of the Code-Behind class used by this page.

Strict

True or False

Uses the Visual Basic Option Strict mode.

WarningLevel

0, 1, 2, or 4

Compiler warning level at which compilation should be aborted.

User Control Properties

You can interact with your user control by exposing a set of properties for it. This will allow you to programmatically change the behavior of the control from the page that is hosting it. It also makes it much easier to build a control that can be used on multiple pages, even if the data being displayed is somewhat different.

There are three steps to using properties with user controls. First, expose the properties from your user control. This is done using the standard property syntax that has already been covered in the book. Let's add the property syntax to the previous example to expose some properties:

 <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <script language="VB" runat="server">  Private ConnStr As String   Property ConnectionString() As String   Get   return ConnStr   End Get     Set   ConnStr = value   End Set   End Property  Sub Page_Load(Source As Object, E As EventArgs)       If Not Page.IsPostBack Then          Dim myConnection As SqlConnection          Dim myCommand    As SqlCommand          Dim myReader     As SqlDataReader          Dim SQL          As String          SQL = "SELECT * FROM Shippers"  If ConnStr = "" Then   ConnStr = "server=localhost;uid=sa;pwd=;database=Northwind"   End If  myConnection = New SqlConnection(ConnStr)       myConnection.Open() 

One of the ways to make the user control more extensible is to not hardcode the database connection string. If the connection string is a property of the control, then the page that is using the control can pass in the proper connection string. In the code you can see that no matter what page the control is used in, a default connection string will be used if none is provided.

The first thing to do is to create a variable to hold the connection string value. Since the property assignment statement is called before Page_Load , create a place to hold the value before it is used to actually open the database:

 Private ConnStr As String 

The next step is to allow the user of the control to set a value for the property, as well as read the value contained by the property. This is done using the Property statement. Provide a Get method to allow for the retrieval of the value, and a Set method to allow the property value to be set.

 Property ConnectionString() As String    Get       return ConnStr    End Get         Set       ConnStr = value    End Set End Property 

Finally, use the connection string to open the database. Since you can't guarantee that a user will have set the Property value, make sure there is a default value that is used if there is no value present. Alternatively, if you require that a value be set, you could throw an exception at this point if there is no value set. But in this example let's use a default value.

 If ConnStr = "" Then    ConnStr = "server=localhost;uid=sa;pwd=;database=Northwind" End If 

The last step is to use this revised user control in your ASP.NET page. To pass a property to a user control, simply add the property name and value as a parameter when adding the user control to the page. These parameters can also be set dynamically in code if desired.

 <wrox:shipment ConnectionString="server=localhost;uid=sa; pwd=;database=Northwind"    runat="server" /> 

User Control Events

The key thing to remember when dealing with user control events is that the event needs to be handled in the user control itself, and not in the page. In this way, the entire event handling for a user control is encapsulated within the control. You should not include event handlers for controls within a user control in the page that is hosting the user control (that is, the parent page) “ the events will not be passed to the page, so the event will never be processed .

Since event handlers within a user control are handled in the same way as event handlers for server controls within pages, this is pretty similar to adding an event handler to a user control (discussed earlier in this chapter). Let's add to the current user control an event that will be fired when the user selects an item from the drop-down list. This event will cause the user's selection to be displayed in a label control just below the selection.

  Sub ShipMethod_Change(Source As Object, E As EventArgs)   SelectedMethod.text = "You have selected " & _   ShipMethod.SelectedItem.Text & _   " as your shipping method."     End Sub   </script>     <asp:DropDownList AutoPostBack="true"   OnSelectedIndexChanged = "ShipMethod_Change"   id="ShipMethod" runat="server"/>   <BR><asp:Label id="SelectedMethod" runat="server"/>  

There are two changes to the DropDownList control that is part of the user control. First, you need to let ASP.NET know that you want to automatically trigger a postback when the selection in the drop-down list is changed. This is done by setting the AutoPostBack attribute to true . The second change is to define what method will be called whenever the user selects an item from the drop-down list. The name of the event that is fired is OnSelectedIndexChanged and when that event is fired, the ShipMethod_Change event is called.

Then you create an event handler within the user control that will be called when the selection is made. This event handler will grab the text value of the selected item from the control, and use that to populate a label control for display back to the user.

 Sub ShipMethod_Change(Source As Object, E As EventArgs)    SelectedMethod.text = "You have selected " & _                          ShipMethod.SelectedItem.Text & _                          " as your shipping method." End Sub 

The SelectedItem property of the DropDownList control identifies the particular item object from the list that is currently selected. The Text property of this object contains the actual text that was in the drop-down list. You can grab this value and use it to build a prompt to display for the user.

Code-Behind with User Controls

Earlier in this chapter, you saw how to use the Page class to create an object that will handle all of the code for the page. Then by placing that class definition into its own file, you can separate the code from the layout. This is the same as the code-behind technique used earlier. Since user controls are very similar to ASP.NET pages, code-behind can be used when creating user controls as well.

The first step is to import the necessary namespaces for the user control. The System and System.Web.UI namespaces are required. Since ASP.NET Server Controls are used as well, import the System.Web.UI.WebControls namespace. In order to retrieve the data from a database, import the System.Data.SqlClient namespace as well:

  Imports System   Imports System.Web.UI   Imports System.Web.UI.WebControls   Imports System.Data   Imports System.Data.SqlClient  

The next step is to declare a class to define the user control. To provide the necessary functionality, this class needs to inherit from the UserControl class. This class will contain the same initialization code, object interface code, and event handling code that is in the user control already created. One important difference is that all variables must be declared Public for each of the server controls that the user control needs.

  Public Class shipMethodClass   Inherits UserControl     Private ConnStr As String   Public ShipMethod As DropDownList   Public SelectedMethod As Label  

When saving the code-behind file, it is important to use the proper filename extension. This is the only way to inform the ASP.NET compiler what language the code-behind file is written in. If you don't use the proper extension, the compiler will fail when trying to display this page.

  <%@ Control Inherits="shipMethodClass" src="shipMethod.vb"   ClassName="shipMethod" %>  

Finally, remove the VB code from the user control and then add a @Control directive to attach the user control to the code-behind file. There are three attributes needed in the @Control directive. The Inherits attribute defines the name of the class that contains the code-behind code. The Src attribute defines the source file that actually contains the code-behind sourcecode. The ClassName attribute defines a class name for the control, needed to dynamically create the control on a page.

Partial Page Caching with User Controls

Caching can reduce the number of processing cycles required to deliver a page when it is requested by the client. By storing the output from a page on the server, and then outputting that information to a client when the same page is requested again, the need to execute the page again is eliminated. A very similar concept will allow us to cache parts of a page.

If you are wondering, "How can I tag part of the page to be cached?", think about what user controls do. They are basically separate page sections that are embedded into another page. If you could cache the output of a user control, and then insert it into a page when it is requested, you can utilize the benefits of caching. This technique is called partial page caching, or fragment caching .

The key to using fragment caching is through user controls. Place the portions of the page to cache into a user control, and then indicate that the user control should be cached. This is done using the @ OutputCache directive, just as when the entire page is cached:

  <%@ OutputCache Duration="  time  " VaryByParam="none" %>  

To see how fragment caching works, let's add caching to the user control created in this chapter. Since this specific user control performs database access, it will gain great performance benefits by being cached. Since the data that is being displayed does not change very frequently, it would be of great benefit if it didn't have to go to the database each time the control is used.

 <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %>  <%@ OutputCache Duration="10" VaryByParam="none" %>  <script language="VB" runat="server">    Sub Page_Load(Source As Object, E As EventArgs)  Dim NowTime As DateTime   Dim Expires as DateTime   NowTime = DateTime.Now   Expires = NowTime.AddSeconds(10)   CreatedStamp.InnerHtml = NowTime.ToString("r")   ExpiresStamp.InnerHtml = Expires.ToString("r")  If Not Page.IsPostBack Then          Dim myConnection As SQLConnection          ... 

The line that enables fragment caching is the @ OutputCache directive. Set the cache to hold the page for 10 seconds. Setting the VaryByParam attribute to none will return the same cached value regardless of the parameters or browser making the request.

The rest of the code added is simply to help identify that the cache is actually working. It will display the time at which the user control was run, and when the cache expires, 10 seconds afterwards. There are two Label server controls that will display that information.

  <HR>Fragment Cache created: <Font color="red">   <B id="CreatedStamp" runat="server"></B></Font>   <BR>   Fragment Cache expires: <Font color="red">   <B id="ExpiresStamp" runat="server"></B></Font>  

When viewing the page in the browser, the time that the page was created is displayed, the time that the user control was created is displayed, and when the cached version of the user control will expire is shown. The time the page was loaded was 15:48:45 “ the same time shown on the fragment cache. After a refresh of the browser, the page creation time changes (it's now 15:49:24), but the user control creation time and cache expire time doesn't change “ it is being drawn from the cache as shown in Figure 4-10:

click to expand
Figure 4-10:



Professional ASP. NET 1.1
Professional ASP.NET MVC 1.0 (Wrox Programmer to Programmer)
ISBN: 0470384611
EAN: 2147483647
Year: 2006
Pages: 243

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net