asp:feature
Languages: VB
Technologies: User Controls | Code-behind | Reflection
User Controls
ASP.NET Makes Reusing Code Easy
By Doug Seven
ASP.NET and the .NET Framework introduce a new structure for building dynamic Web applications. In previous incarnations of ASP, developers became accustomed to working with server-side includes as a means of easy code reuse. They were the best thing available at the time, but using includes still came with problems:
- Includes were processed in the same space as the page they were on, so variable conflicts were frequent.
- An include file could be used only once on a page, so making controls that appeared multiple times on a page was cumbersome.
- Includes did not support an object model that allowed for exposing properties and methods.
Although ASP.NET does support the use of includes, it also introduces a new means of encapsulating code for reuse: the user control.
In this article, you will learn what a user control is, what its advantages are, and how to build and interact with it. You ll start with some basic examples and build on them until you have made a complex user control that exposes properties and methods.
What Is a User Control?
The .NET Framework SDK documentation defines a user control as a user-authored server control that enables an ASP.NET page to be reused as a server control. A few modifications can turn any ASP.NET Web Form into a user control you ll learn more about that later. For now, understand that in its simplest form, a user control is no more than text saved in a separate file and used on a Web Form like any server control.
User controls provide an easy means of writing reusable logic and user-interface code. You can write user controls in any text editor, as you can Web Forms. However, you must save user controls with the extension .ascx. The .NET Framework identifies them as user controls based on this extension. User controls are compiled the first time they are used and are saved in memory the same way Web Forms are. One of the great advantages user controls have over includes is that user controls support the ASP.NET object model, which means they can expose properties and methods like any other object in the .NET Framework. User controls are designed for use in a single Web application, and are not designed to port across application boundaries. For reusable components that encapsulate logic and UI and are for use in multiple applications, use custom server controls.
Building a Basic User Control
As I said before, a user control is, in its simplest form, just text saved in a separate file that you can use on any Web Form in your application. You cannot navigate to user controls independently because they are only accessible as controls on a Web Form. Because of this, user controls should never contain , , and elements. Those elements will be in the Web Form.
To build a simple user control, open your favorite text editor and create a new file named MyUserControl.ascx. In this file, type the text This is my user control and save the file. That s it. You have created a user control. To use it, register it on a Web Form, which will enable you to place it on the Web Form just like any ASP.NET server control. Create a new Web Form named WebForm1.aspx in the same directory as the user control and add the code from FIGURE 1.
<%@ Register TagPrefix="DotNetJunkies"
TagName="MyUserControl"
Src="MyUserControl.ascx" %>
FIGURE 1: Registering a user control on a Web Form.
With this code, you register the user control on the Web Form using the @ Register directive. The three attributes of the @ Register directive are:
- TagPrefix The prefix used when placing the control on the Web Form, similar to how asp is used to prefix intrinsic server controls.
- TagName The name by which the control is referenced.
- Src The absolute or relative location of the user control.
When you
want to place the user control on the page, use the syntax
Exposing Properties
As with any object in the .NET Framework, user controls can expose properties. Once a user control exposes a property, you can access it either declaratively or programmatically. Creating properties for a user control is the same as creating properties for any object. Properties can be created as Public instances of a variable, or as Property objects with Get and Set methods.
Change the MyUserControl.aspx file to include the following code:
<%=Text %>
With this code, you create a public string variable named Text. This variable is accessible outside the user control because you declared it publicly. The code uses the <%= %> Response.Write shortcut to write the value of the Text variable. In the Web Form, you can set the value of the Text variable declaratively, as follows:
Text="This is the Text
property." /> The resulting page is shown in FIGURE 2. One of
the advantages user controls have over include files is that user controls are
processed in their own sandboxes. In other words, each instance of a user
control is in an isolated processing space, which protects it from any variable
conflicts. This enables multiple instances of the same user control on one Web
Form, without any conflicts. Change WebForm1.aspx to use the code from FIGURE
3. <%@ Register
TagPrefix="DotNetJunkies" TagName="MyUserControl" SRC="MyUserControl.ascx" %>
FIGURE 2: The user control is
rendered as an HTML element within the page.
FIGURE 3: Using multiple instances of one user control.
In this Web Form, you use the same user control three times and provide a separate value for the Text property with each one. If you had tried to use the same include file three times in one ASP page, you would have raised the following error:
Microsoft VBScript compilation (0x800A0411)
Name redefined.
Because each user control instance is processed in its own sandbox, no variable conflicts occur. Instead, you get the page shown in FIGURE 4.
FIGURE 4: Each user control
instance is rendered appropriately, without conflicts.
Properties in user controls also can be exposed as Property objects with Get and Set methods. This allows for code execution to occur when a property is either set or read. Create a new user control named MyOtherUserControl.ascx and add the code shown in FIGURE 5.
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
Font-Bold="True" /> FIGURE 5:
Exposing properties in a user control. The code
in FIGURE 5 creates a new user control that exposes two properties, Text and Table. In the Text
property declaration, set the value of the Text
property (the value that was set either declaratively or programmatically) to
the Text property of a Label control. In the Get method of the property, you return
the value of the Text property of
the Label control. For a
more complex example of how you can use Property
objects to execute code when a property is set, look at the Table property in FIGURE 5. First, you
declare a private variable, _table,
which is set to the value of the Table
property. The variable will maintain the value of the Table property for easy access in the user control. You use the
value that was set to dynamically create a T-SQL SELECT command and
execute that command on a SqlConnection,
calling SqlCommand.ExecuteReader to
return a SqlDataReader object. That
object is set as the DataSource
property of a DataGrid. You bind the
DataGrid and close the connection.
When the user control s Table
property is set, this code executes, and the DataGrid is bound to the returned SqlDataReader. In the Get
method of the Table property, you
return the value of _table. To use
this new user control, create a new Web Form named WebForm2.aspx and add the
code from FIGURE 6. <%@ Register
TagPrefix="DotNetJunkies" TagName="MyOtherUserControl" SRC="MyOtherUserControl.ascx"
%>
FIGURE 6: Setting user control properties declaratively.
In the Web Form, register the user control and place two instances of it on the page. In each instance, you declaratively set the values of the two publicly exposed properties. Notice that if you attempt to set the _table variable from the Web Form, nothing will happen. This is because you declared the _table variable privately, and it is not accessible outside the user control.
When you request this Web Form in a browser, the two properties for the user controls are set, and the code in the Set method executes. See the resulting page, shown in FIGURE 7.
FIGURE 7: The user control
properties determine the user control output.
Exposing Methods
Just as user controls can expose properties, they also can expose methods. By exposing a public method in the user control, you can achieve the same functionality the Table property managed in the previous user control. Create a new user control named MyThirdUserControl.ascx and add the code shown in FIGURE 8.
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
Font-Bold="True"
/> FIGURE 8:
Exposing methods in user controls. In this
code, you move the data-access functionality to a public method named GetData. This method takes the table
name as an argument and executes the same code from the previous example. You can
invoke publicly exposed methods of user controls programmatically from a Web
Form. Create a new Web Form named WebForm3.aspx and add the code from FIGURE 9. <%@ Register
TagPrefix="DotNetJunkies" TagName="MyThirdUserControl" SRC="MyThirdUserControl.ascx"
%>
FIGURE 9: Programmatically invoking user control methods.
In WebForm3.aspx, you register the user control as in previous examples. When you place the user control on the page, you add an ID value to it so you can access it programmatically, like any control in ASP.NET. In the Page_Load event handler for the Web Form, you set the Text property of the user control by using the syntax:
ID.Property = Value
Next, you invoke the GetData method with the syntax:
ID.Method(arguments)
The resulting page looks identical to the page you saw in FIGURE 7.
The Benefits of Code-behind
User controls can be tied to a code-behind class in the same way Web Forms can. This allows for a clean separation of code and content. When using code-behind with a Web Form, the code-behind class is defined in the Page directive. With user controls, the Control directive is used because a user control does not inherit from the System.Web.UI.Page class, but rather from the System.Web.UI.UserControl class. The attributes for the Control directive are the same as for the Page directive, with the exception of the Trace attribute which is not available in the Control directive. Tracing must be enabled at the page level, not the control level.
You can convert the user control MyThirdUserControl.ascx to use code-behind by making a few modifications. First, you must move all the programmatic code to a separate code-behind file named MyThirdUserControl.ascx.vb. (This follows the Visual Studio .NET naming convention.) The code for this file can be found in FIGURE 10.
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls
Imports System.Data
Imports System.Data.SqlClient
Public Class MyThirdUserControl : Inherits UserControl
Protected TextLabel As Label
Protected myGrid As DataGrid
Public Property Text As String
Get
Return TextLabel.Text
End Get
Set
TextLabel.Text = Value
End Set
End Property
Public Sub GetData(_table As String)
Dim SqlStmt As String = "SELECT TOP 2 * FROM " & _table
Dim conString As String = _
"server=localhost;database=Northwind;uid=sa;pwd=;"
Dim con As SqlConnection = New _
SqlConnection(conString)
Dim cmd As SqlCommand = New _
SqlCommand(SqlStmt, con)
con.Open()
myGrid.DataSource = cmd.ExecuteReader()
myGrid.DataBind()
con.Close()
End Sub
End Class
FIGURE 10: Creating a user control with a code-behind class.
The user control code-behind class looks nearly identical to a Web Form code-behind class. The main difference is that the user control code-behind class inherits from System.Web.UI.UserControl rather than System.Web.UI.Page. The .ascx file is now stripped of all programmatic code, leaving only UI code. The Control directive is needed to tell the JIT compilers what code-behind class the user control inherits. And, if the code-behind class is not precompiled, you use the Src attribute to tell the JIT compiler where to find the class file:
<%@ Control Inherits="MyThirdUserControl"
SRC="MyThirdUserControl.ascx.vb" %>
Font-Bold="True" /> The Control directive uses the Inherits
attribute to identify the code-behind class this user control inherits, and the
Src attribute is used to tell the Visual Basic .NET JIT compiler where
to find the class file. When a Web Form that includes this user control is
requested, the code-behind class is JIT-compiled and saved in memory. Using Reflection to Discover User Control Properties and Methods User
control properties and methods can be accessed programmatically from a Web Form
code-behind class, also. There are two ways to do this: If the user control is
precompiled, declare the user control in the code-behind class of the Web Form
as an instance of the class: Protected UC1 As MyUserControl If the
user control is JIT compiled, as in the previous examples, you must use
reflection to discover the class type of the user control and what properties
and methods it exposes. This is because the .NET Framework does not know
anything about the user control class at compile time because the class has not
been compiled yet. In this case, you must declare the user control in the Web
Form code-behind file as an instance of its base type, System.Web.UI.UserControl: Protected UC1 As UserControl Using
reflection, the .NET Framework can discover and understand the class type,
methods, and properties. Dealing with JIT-compiled user controls in a Web Form
code-behind class takes a little more code, but not much. The code in FIGURE 11
is the code-behind class for WebForm4. Save this file as WebForm4.aspx.vb. Imports System Imports
System.Web Imports
System.Web.UI Imports
System.Web.UI.WebControls Imports
System.Web.UI.HtmlControls Imports
System.Reflection Public Class WebForm4 : Inherits Page Protected UC1 As
UserControl Protected UC2 As UserControl Protected Sub Page_Load(Sender As Object, E
As EventArgs) Dim _uc1Type As Type = UC1.GetType() Dim _uc2Type As Type = UC2.GetType() Dim _uc1Text As
PropertyInfo = _ _uc1Type.GetProperty("Text") Dim _uc1GetData As
MethodInfo = _ _uc1Type.GetMethod("GetData") _uc1Text.SetValue(UC1, "This is the
Customers Table", _ Nothing) Dim argumentArray(0) As
string argumentArray(0) = "Customers" _uc1GetData.Invoke(UC1, argumentArray) Dim _uc2Text As
PropertyInfo = _ _uc2Type.GetProperty("Text") Dim _uc2GetData As
MethodInfo = _ _uc2Type.GetMethod("GetData") _uc2Text.SetValue(UC2, "This is the
Products Table", _ Nothing) argumentArray(0) = "Products" _uc2GetData.Invoke(UC2, argumentArray) End Sub End Class FIGURE 11:
Setting user control properties and invoking methods from code-behind using
reflection. In
FIGURE 11 you declared two user controls, UC1 and UC2, as
instances of the System.Web.UI.UserControl class. In the Page_Load event handler for the Web
Form, you use reflection to discover the class types and the publicly exposed
properties and methods of these user controls. First,
you create a Type object and set it to the return value of the GetType
method exposed by the user controls. All objects in the .NET Framework expose
this method. It is inherited from the base System.Object class from
which all objects inherit. Using
this Type object, you can probe for properties and methods as PropertyInfo
and MethodInfo objects. The PropertyInfo
objects expose a SetValue method.
The SetValue method takes three
arguments: the ID of the object whose value is being set, the value to set, and
an optional argument of index values for use when setting indexed properties.
In FIGURE 11 SetValue is invoked
once for each user control, UC1 and UC2,
to set their Text property values. The
Visual Basic .NET keyword Nothing is
used for the third argument because you are not dealing with indexed
properties. The MethodInfo object exposes an Invoke method. This method takes two
arguments, the ID of the control whose method is being invoked, and an object
array of method arguments. For the GetData
method the user control exposes only one argument is passed into the method
when it is invoked: the name of the table for the SELECT command. The PropertyInfo and MethodInfo objects are used, and the PropertyInfo.SetValue
and MethodInfo.Invoke methods are invoked for each user control. The Web
Form (WebForm4.aspx) that goes along with this code-behind class simply
inherits from the class and places two instances of the MyThirdUserControl user control on the form (see FIGURE 12). <%@ Page
Inherits="WebForm4" Src="WebForm4.aspx.vb" %> <%@ Register
TagPrefix="DotNetJunkies" TagName="MyThirdUserControl" SRC="MyThirdUserControl.ascx" %>
FIGURE 12: Using multiple instances of one user control.
When you browse to the WebForm4.aspx, the resulting page is identical to that shown in FIGURE 7.
Using Precompiled User Controls and Known Classes
Using precompiled code-behind classes with user controls does not require the use of reflection because the user control s class type is known at compile time. Because the user control s type, properties, and methods also are known at compile time, they are directly accessible in the Web Form code-behind class, as shown in FIGURE 13.
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls
Public Class WebForm5 : Inherits Page
Protected UC1 As CompiledUC
Protected UC2 As CompiledUC
Protected Sub Page_Load(Sender As Object, E As EventArgs)
UC1.Text = "This is the Customers Table"
UC1.GetData("Customers")
UC2.Text = "This is the Products Table"
UC2.GetData("Products")
End Sub
End Class
FIGURE 13: Setting precompiled user control properties programmatically from code-behind.
The code example in FIGURE 13 assumes the class name for the user control was changed from MyThirdUserControl to CompiledUC, and the user control s code-behind class was compiled into a DLL and placed in the application s \bin folder. Because the type CompiledUC is known when the Web Form is requested, the user control instances can be mapped directly to this class. As a result, the Text property and GetData method are known and can be called directly without using reflection.
Conclusion
User controls provide an easy means of writing reusable code and user interface elements. Like Web Forms, user controls can be created using any text editor. And, unlike custom server controls, they do not have to be compiled. In its simplest form, a user control is just text that may be reused in an application in many places. In more complex user controls, properties and methods may be exposed, and complex code execution can occur. User controls support the .NET object model, which allows for more power and flexibility than include files provide. Also, each user control is processed in its own sandbox, which prevents naming and variable conflicts when more than one instance of the user control appears in a single Web Form.
User controls are designed and intended for building reusable code modules whether logic only or logic and UI for use in a single Web application. When you need reusable code elements that can span application or machine boundaries, you should use custom server controls instead of user controls.
The files referenced in this article are available for download.
Doug Seven is a co-founder of the .NET training company DotNetJunkies.com. He came to DotNetJunkies.com by way of technical roles at Nordstrom, Microsoft, and GiftCertificates.com and a position as training specialist at Seattle Coffee Company. Seven co-authored Programming Data-Driven Web Applications with ASP.NET and ASP.NET: Tips, Tutorials, & Code, and is currently working on Professional ADO.NET and Building Custom Controls for ASP.NET. Readers may contact him at mailto:[email protected].
Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.