Custom Controls


Sams Teach Yourself ASP.NET in 21 Days, Second Edition
By Chris Payne
Table of Contents
Day 6.  Learning More About Web Forms

The ASP.NET framework also allows you to create custom controls. This is an advanced topic, but you should be comfortable learning about custom controls because you're familiar with the Web forms framework.

Custom controls are not user controls. Rather than encapsulating some prebuilt UI functionality, custom controls can define their own, completely original behavior. For instance, ASP.NET doesn't provide a built-in control for rendering line art. A developer could build a custom control to provide this functionality, and it could easily be reused and placed in any ASP.NET application.

Whereas user controls are used when you want to combine the functionality of existing controls, custom controls are used when none of the existing controls meet your needs. The Web forms framework allows you to extend the library of controls with your own controls. Custom controls can be completely original, or they can extend an existing control's behavior. At this point, it will be helpful to examine the Web forms framework in more detail. Figure 6.5 illustrates the relationships between the different concepts in the Web forms framework.

Figure 6.5. The hierarchy of objects in the Web forms framework.


All objects in the Web forms framework are derived from the System.Web.UI.Control class, directly or indirectly. This class provides the basic methods and properties common to all controls, such as ID, GetType, and ToString. Each object under the Control object provides additional functionality.

The Web forms framework allows you to insert a custom control anywhere in this hierarchy, as long as it's derived from the Control class somehow. You can extend existing functionality, or simply create new mechanisms that reside in one of the namespaces shown in Figure 6.5. Typically, you'll derive from the WebControl or HtmlControl classes because they provide the most prebuilt functionality for displaying user interfaces.

Creating Custom Controls

Let's build a custom control. Listing 6.6 shows a class that's derived from the Control class and displays a simple message to the user. Listing 6.7 shows the C# version of the code.

Listing 6.6 A Custom Control Is Nothing More Than a VB.NET Class
 1:    Imports System 2:    Imports System.Web 3:    Imports System.Web.UI 4: 5:    Namespace MyCustomControls 6:       Public Class CustomControl1 : Inherits Control 7:          Protected Overrides Sub Render(Output as HtmlTextWriter) 8:             Output.Write("This is my custom control! The time is now " & DateTime.Now. graphics/ccc.gifToString) 9:          End Sub 10:       End Class 11:    End Namespace 
Listing 6.7 Your Custom Control in C#
 1:    using System; 2:    using System.Web; 3:    using System.Web.UI; 4: 5:    namespace MyCustomControls { 6:       public class CustomControl1 : Control { 7:          protected override void Render(HtmlTextWriter Output) { 8:             Output.Write("This is my custom control! The time is now " + DateTime.Now. graphics/ccc.gifToString()); 9:          } 10:       } 11:    } 


Save this file as CustomControl1.vb (or CustomControl1.cs for the C# version). Notice that this is a VB.NET or C# source file, not an ASP.NET page. Therefore, you have to perform a few additional steps, such as importing namespaces that were automatically imported for .aspx files. On lines 1 3, you import these namespaces. The Imports keyword (using in C#) is similar to the @ Import directive for ASP.NET pages.

Next, on line 5, you declare the namespace that this control will belong to. This can be any made-up namespace you want. Just remember to include the End Namespace or closing bracket, as shown on line 11. On line 6, you create your class that's derived from the Control class. (See Day 3, "Using Visual Basic.NET and C#," for more information on creating classes.)

Lines 7 9 contain one of the most important parts of user controls. Render is the method that allows existing controls to display HTML in the browser. This method is inherited by all server controls from the Control class. In order to get your custom control to display HTML in the browser, you must provide an implementation for this method. However, since this method already exists in the Control class, you must override it using the Overrides keyword in VB.NET, or override in C#. (Built-in server controls do this as well.) Don't forget the Protected keyword!

The Render method takes one parameter, an HtmlTextWriter object named Output. This object provides all the methods needed to help the Render method produce HTML output. You'll use it to spit out the display for your custom control. On line 8, you use the Write method of the Output variable to send HTML to the browser. It's the current time and a welcome message in this case.

Finally, to use your custom control in another ASP.NET page, you must compile the code ASP.NET cannot use VB.NET or C# source files directly. Open a command prompt, navigate to the directory containing CustomControl1.vb, and type the following:

 vbc /t:library /out:..\..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl1.vb 

This will compile the CustomControl1.vb source file into an MSIL file named CustomControls.dll (as specified by the /out parameter) for ASP.NET to use. For the C# version, the command is nearly identical:

 csc /t:library /out:..\..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl1.cs 

You should see something similar to the following output:

 Microsoft (R) Visual Basic.NET Compiler version 7.00.9114 for Microsoft (R) .NET CLR version 1.00.2523 Copyright  Microsoft Corp 2000. All rights reserved. 


Make sure the c:\inetpub\wwwroot\bin directory already exists, or you'll receive an error when you try to compile your code.

For more information on namespaces, classes, compilation commands, the \bin directory, and creating objects in general, see Day 15, "Using Business Objects."

Using Custom Controls

Once the custom control is built and compiled, you can implement it on any page, just as you would with a user control. Again, you must use the @ Register directive, but this time the syntax is slightly different:

 <%@ Register TagPrefix="ACME" Namespace="MyCustomControls" Assembly="CustomControls"%> 

The TagPrefix attribute remains the same as with user controls. However, TagName and src have been replaced with the Namespace and Assembly attributes. Namespace is simply the namespace you created in your custom control on line 5 of Listing 6.6: MyCustomControls. The Assembly attribute specifies the compiled source file, CustomControls. (Notice there's no .dll extension. ASP.NET assumes this when you use the Assembly element.) You can then implement this control in your ASP.NET page just like any other control. Listing 6.8 shows a simple example of using this control.

Listing 6.8 Implementing Your Custom Control Is Exactly Like Implementing User and Server Controls
 1:    <%@ Page Language="VB" %> 2:    <%@ Register TagPrefix="ACME" Namespace="MyCustomControls"  graphics/ccc.gifAssembly="CustomControls"%> 3: 4:    <html><body> 5:       <form runat=server> 6:          The custom control produces the following output:<p> 7: 8:          <ACME:CustomControl1  runat=server/> 9:       </form> 10:    </body></html> 

Note that this code will work regardless of the language you built your custom control in. This listing produces the output in Figure 6.6 when viewed in a browser.

Figure 6.6. The custom control outputs the HTML specified in the Write method of the HtmlTextWriter object.


Using Properties and State

So far, you've only looked at using custom controls to write HTML to the browser. Just like other server controls, though, custom controls can have properties, methods, and events, can participate in form posts, and can even maintain state.

To add a property to your control, simply declare a public variable or a property element in your custom control class. For example, you could modify the class in Listing 6.6 so it looks like Listing 6.9.

Listing 6.9 Adding Properties to Your Custom Control
 6:    Public Class CustomControl1 : Inherits Control 7:       private strMessage as string = "This is my custom control!" 8:       public property Message as string 9:          Get 10:             Message = strMessage 11:          End Get 12:          Set 13:             strMessage = value 14:          End Set 15:       end property 16: 17:       Protected Overrides Sub Render(Output as HtmlTextWriter) 18:          Output.Write(strMessage & " The time is now " & _ 19:             DateTime.Now.ToString) 20:       End Sub 21:    End Class 


On line 7, you create a private variable, strMessage, that holds the text to display to the user. On lines 8 15, you create a public property that can be used to set and retrieve the value from strMessage. (See "Enhancing the User Control" earlier today for information on how to create properties.) In the Render method, you've modified the code to display the text contained in strMessage, rather than the static text that was there originally.

Recompile this custom control with the command prompt command:

 vbc /t:library /out:..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl1.vb 

Modify Listing 6.8 to utilize the new Message property:

 <ACME:CustomControl1  runat=server    Message="Hello world!" /> 

Listing 6.8 will now display the new message, as shown in Figure 6.7.

Figure 6.7. Adding a property to your custom control enhances its functionality.


If you want the control to maintain state, you simply have to add the items to the state bag (see Day 4, "Using ASP.NET Objects with C# and VB.NET"). Listings 6.10 and 6.11 show a new custom control that implements state management.

Listing 6.10 Creating a Custom Control with State Management
 1:    Imports System 2:    Imports System.Web 3:    Imports System.Web.UI 4: 5:    Namespace MyCustomControls 6:       Public Class CustomControl2 : Inherits Control 7:          public property Message as string 8:             Get 9:                Message = ViewState("Message").ToString 10:             End Get 11:             Set 12:                ViewState("Message") = value 13:             End Set 14:          end property 15: 16:          public property Size as integer 17:             Get 18:                Size = CType(ViewState("Size"), Integer) 19:             End Get 20:             Set 21:                ViewState("Size") = value 22:             End Set 23:          end property 24: 25:          Protected Overrides Sub Render(Output as HtmlTextWriter) 26:             Output.Write("<font size=" & Me.Size & ">" & _ 27:                Me.Message & "</font>") 28:          End Sub 29:       End Class 30: 31:    End Namespace 
Listing 6.11 Creating a Custom Control with State Management
 1:    using System; 2:    using System.Web; 3:    using System.Web.UI; 4: 5:    namespace MyCustomControls { 6:       public class CustomControl2 : Control { 7:          public string Message { 8:             get { 9:                return ViewState["Message"].ToString(); 10:             } 11:             set { 12:                ViewState["Message"] = value; 13:             } 14:          } 15: 16:          public int Size { 17:             get { 18:                return (int)ViewState["Size"]; 19:             } 20:             set { 21:                ViewState["Size"] = value; 22:             } 23:          } 24: 25:          protected override void Render(HtmlTextWriter Output) { 26:             Output.Write("<font size=" + this.Size + ">" + this.Message + "</font>"); 27:          } 28:       } 29: 30:    } 


Save these files as CustomControl2.vb and CustomControls2.cs, respectively. This control displays a simple message to the user, specified by the Message property. As such, it's very similar to CustomControl1.vb. However, the Message property is now maintained in state. In the Set element on line 12, you persist the supplied value to a variable in the state bag named Message, rather than to a private variable as in Listing 6.9. The Get statement on line 9 then retrieves this value from the state bag. Remember to use the ToString method to convert the value returned from state to a string, because ASP.NET returns it as an object by default.

You've added a new property, Size, which specifies the font size to render the message in. This property element works very similarly to the Message property, except that it's an integer. Again, the Get and Set elements retrieve and assign the value to the state bag. The CType method (or the int casting operator) on line 18 converts the data returned from the state bag to an integer.

Compile this file with the following command:

 vbc /t:library /out:..\ ..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl2.vb 

or for C#:

 csc /t:library /out:..\ ..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl2.cs 

Listing 6.12 shows an ASP.NET page that uses this new control.

Listing 6.12 Implementing the State Maintenance Custom Control on an ASP.NET Page
 1:    <%@ Page Language="VB" %> 2:    <%@ Register TagPrefix="ACME" Namespace="MyCustomControls"  graphics/ccc.gifAssembly="CustomControls"%> 3: 4:    <script runat="server"> 5:       sub Submit(Sender as Object, e as EventArgs) 6:          MyControl.Size = MyControl.Size + 1 7:       end sub 8:    </script> 9: 10:    <html><body> 11:       <form runat=server> 12:          The custom control produces the following output:<p> 13: 14:          <ACME:CustomControl2  runat=server 15:             Message="Hello world!" 16:             Size=1 /> 17: 18:          <asp:Button runat="server" 19:             Text="Increase size!" 20:             OnClick="Submit"/> 21:       </form> 22:    </body></html> 


Your custom control is implemented on line 14, with values specified for both the Message and Size properties. When a user submits the form by clicking on the button on line 18, the Submit method (line 5) increases the size of the displayed message by 1, using the Size property of the custom control.

Note that this can occur only if state management is enabled for the custom control. ASP.NET needs to know what the size was before the post to be able to increase it by 1. If state isn't maintained, MyControl.Size will always return 1; that is, the value set on line 16. But the size increases with every form post, indicating that the control is maintaining state. Figure 6.8 shows the output after you've clicked the button several times.

Figure 6.8. The increasing font size is evidence that state is maintained.


Wiring Up Events

One of the most important aspects of server controls is that they react to the user's actions. Building this functionality into custom controls isn't difficult, but you need to know how server controls function when they're requested by a client. All server controls follow the order of execution described here. Note that this doesn't list all of the steps, but only the most important ones:

  1. Load state information.

  2. Process postback data. The control examines any incoming data from a form post.

  3. Load the control. This is similar to the Page_Load event.

  4. If form data has changed, raise events to handle it.

  5. Handle the event that caused the postback.

  6. Save the state of the controls.

  7. Render the output to the browser.

Notice that there are two separate steps to handle events related to postback data. First, you must detect the changes in data, as shown in step 2. Then you execute the methods that handle these changes, as shown in step 4. This two-step process translates directly to the methods that must be accomplished so your custom controls can handle events. You determine if an event needs to be raised, and then you call a method to raise it. The event can then be handled by the ASP.NET page that implements this control. Figure 6.9 illustrates this procedure.

Figure 6.9. The steps required to handle events in custom controls.


To build an event for a custom control, first you must decide which event you want to handle. Do you want to react to changes in a text box? A button click? The type of data generated by the event plays a part in how you implement the functionality in the user control. You simply provide implementations for steps 2 and 4. Luckily, the work is very cut-and-dried, and you don't have to write much code yourself. Let's take a look at a new custom control that contains events, shown in Listings 6.13 and 6.14.

Listing 6.13 An Event-Wary Custom Control
 1:    Imports System 2:    Imports System.Web 3:    Imports System.Web.UI 4:    Imports System.Collections.Specialized 5: 6:    Namespace MyCustomControls 7:       Public Class CustomControl3 : Inherits Control : Implements IPostBackDataHandler 8:          public Event TextChanged(obj as object, e as eventargs) 9:          protected sub OnTextChanged(e as EventArgs) 10:             RaiseEvent TextChanged(Me, e) 11:          end sub 12: 13:          Public Function LoadPostData(PostDataKey As String, Values As  graphics/ccc.gifNameValueCollection) As Boolean Implements IPostBackDataHandler.LoadPostData 14:             dim strOldValue as String = Me.Message 15:             dim strNewValue as String = Values(postDataKey) 16:             if not strOldValue = strNewValue 17:                Me.Message = strNewValue 18:                return true 19:             end if 20:             return false 21:          End Function 22: 23:          Public Sub RaisePostDataChangedEvent() Implements IPostBackDataHandler. graphics/ccc.gifRaisePostDataChangedEvent 24:             OnTextChanged(EventArgs.Empty) 25:          end sub 26: 27:          public property Message as string 28:             Get 29:                Message = ViewState("Message").ToString 30:             End Get 31:             Set 32:                ViewState("Message") = value 33:             End Set 34:          end property 35: 36:          Protected Overrides Sub Render(Output as HtmlTextWriter) 37:             Output.Write("<input name=" & Me.UniqueID & _ 38:                " type=text value=""" & Me.Message & """>") 39:          End Sub 40:       End Class 41: 42:    End Namespace 
Listing 6.14 An Event-Wary Custom Control in C#
 1:    using System; 2:    using System.Web; 3:    using System.Web.UI; 4:    using System.Collections.Specialized; 5: 6:    namespace MyCustomControls { 7:       public class CustomControl3 : Control, IPostBackDataHandler { 8:          public event EventHandler TextChanged; 9:          protected virtual void OnTextChanged(EventArgs e) { 10:             TextChanged(this, e); 11:          } 12: 13:          public bool LoadPostData(String PostDataKey, NameValueCollection Values) { 14:             string strOldValue = this.Message; 15:             string strNewValue = Values[PostDataKey]; 16:             if (strOldValue != strNewValue) { 17:                this.Message = strNewValue; 18:                return true; 19:             } 20:             return false; 21:          } 22: 23:          public void RaisePostDataChangedEvent() { 24:             OnTextChanged(EventArgs.Empty); 25:          } 26: 27:          public string Message { 28:             get { 29:                return ViewState["Message"].ToString(); 30:             } 31:             set { 32:                ViewState["Message"] = value; 33:             } 34:          } 35: 36:          protected override void Render(HtmlTextWriter Output) { 37:             Output.Write("<input name=" + this.UniqueID + " type=text value=\"" + 38:  graphics/ccc.gif   this.Message + "\">"); 39:          } 40:       } 41: 42:    } 


Save these files as CustomControl3.vb and CustomControl3.cs. There appears to be a lot of complex functionality here, but don't worry! It's actually very simple once you dissect it. The first thing to notice is Implements IPostBackDataHandler on line 7 (the Implements keyword is represented by a comma in C#). IPostBackDataHandler is an interface that defines how controls interact with postback information; it provides a blueprint for how to write your code. This is a standard item that's required if you want to handle events, so don't worry about it too much. Once you learn the syntax, the rest is easy.

Next, on line 8, you declare an event. This is very similar to declaring a variable, except that you must use the event keyword. In this case, you're going to handle the text changing in your custom control, hence the event name TextChanged. Notice that you supply the standard event parameter list here as well for VB.NET; for C# it's not necessary.

On lines 9 11, you declare a subroutine, OnTextChanged, that raises the TextChanged event. Recall that in your ASP.NET pages, you use the syntax OnEventName to specify event handlers for your controls (for example, OnTextChanged="HandleChange"). On lines 9 11, you define this On event. For VB.NET you need to use the RaiseEvent method to raise an event, which causes the TextChanged event to fire with the specified parameters. In C#, you can simply call the event name. Again, this code is standard. All you need to do to customize it for your controls is change the names of the events.

Next is the LoadPostData method, which corresponds to step 2 of a control's order of execution. This method takes two parameters: the first specifies the name of the control whose data is being examined (this will always be the custom control), and the second specifies the data that the user submitted in the form post. The LoadPostData method returns true if you want the event to be handled, and false otherwise. The declaration of this method is pretty long, but it's standard.

Inside this method, on lines 14 20, you examine the posted data. You retrieve the old value of the control on line 14 by accessing the Message property of your control. You then retrieve the new data by accessing it from the second parameter in the LoadPostData method. On line 16, you compare these values to determine if the data has changed. If it has, you return true, which tells ASP.NET that a method must be executed to handle this event. Otherwise, you return false.

Finally, the RaisePostDataChangedEvent on line 23 calls the methods to handle the event. This is another long declaration, but again, it's standard. It simply calls the OnTextChanged method, which in turn raises the TextChanged event, which can then be handled in your ASP.NET page.

The rest of the listing is almost exactly the same as Listing 6.9. The only difference is that you now have to provide a name property for the control that will generate the event, as shown on line 37. Use the UniqueID property to return the name of the custom control. Without this step, your control won't generate any events.

Also, don't forget to import the System.Collections.Specialized namespace, as shown on line 4, and change the class's name to CustomControl3, as shown on line 7.

Let's take a brief moment to recap:

  • You first created an event using the event keyword, and then you created a method using the OnEvent syntax that raises the event with the RaiseEvent method.

  • Next, you used the standard LoadPostData method to determine if the data has changed and if it warrants raising an event.

  • The RaisePostDataChangedEvent method is responsible for calling another method that raises the event you defined in step 1.

Much of the syntax used here is standard, and so is the procedure to implement it.

Now that the custom control is coded, let's compile the VB.NET version using the following command:

 vbc /t:library /out:..\ ..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl3.vb 

And the C# version with

 csc /t:library /out:..\..\bin\CustomControls.dll /r:System.dll /r:System.Web.dll  graphics/ccc.gifCustomControl3.cs 

Listing 6.15 shows an ASP.NET page that implements this custom control.

Listing 6.15 Handling the Custom Control's Events in an ASP.NET Page
 1:    <%@ Page Language="VB" %> 2:    <%@ Register TagPrefix="ACME" Namespace="MyCustomControls"  graphics/ccc.gifAssembly="CustomControls"%> 3: 4:    <script runat="server"> 5:       sub Submit(obj as object, e as eventargs) 6:          'do nothing 7:       end sub 8: 9:       sub ChangeIt(obj as object, e as eventargs) 10:          Response.write("Event handled!") 11:       end sub 12:    </script> 13: 14:    <html><body> 15:       <form runat=server> 16:          The custom control produces the following output:<p> 17:          <ACME:CustomControl3  runat="server" 18:             Message="Hello world!" 19:             OnTextChanged="ChangeIt" /> 20:          <asp:Button runat="server" 21:             Text="Submit" 22:             OnClick="Submit" /> 23:       </form> 24:    </body></html> 


Now, it's time for some easy stuff! This page is just like any other ASP.NET page that you've developed, so it should be a breeze compared to Listing 6.12. Moving to line 17 first, you see the implementation of your custom control. This looks just like the implementation for the previous custom control you developed, but now it has OnTextChanged="ChangeIt" on line 19. According to Listing 6.12, this line will cause the ChangeIt method to execute whenever the data in the text box has changed (after the form is posted, that is). On line 20, you create a simple button control that will cause the form to be posted. It calls the Submit method on line 5, which is just a place holder. It doesn't actually do anything.

The ChangeIt method on line 9 writes a message to the browser that the event has fired and has been handled. View this page from the browser, and experiment by submitting the form and changing the text values. Notice that if you don't change the text in the text box, the ChangeIt method isn't executed, which means that the event didn't fire. Figure 6.10 shows the output after you change the text and click the button.

Figure 6.10. Handling the user control's events from an ASP.NET page.


There's a lot more you can do with custom controls. For instance, you can cause normally non-posting elements (such as text boxes or images) to post automatically during an event. You can create existing server controls inside your custom control, to do with as you want. You can even make your custom control generate JavaScript to perform client-side event handling.

This is just the tip of the iceberg, but now you know the basics of creating and using fully functional custom controls. For more information, see the .NET Framework documentation or the ASP.NET Quickstart samples supplied with the .NET SDK.


    Sams Teach Yourself ASP. NET in 21 Days
    Sams Teach Yourself ASP.NET in 21 Days (2nd Edition)
    ISBN: 0672324458
    EAN: 2147483647
    Year: 2003
    Pages: 307
    Authors: Chris Payne

    Similar book on Amazon © 2008-2017.
    If you may any questions please contact us: