Adding Child Controls to a Control


Several of the standard ASP.NET controls, such as ListBox and RadioButtonList , are declared using child controls. For example, you declare a ListBox control with ListItem controls like this:

 
 <asp:ListBox   Runat="Server">   <asp:ListItem Text="First Item" />   <asp:ListItem Text="Second Item" />   <asp:ListItem Text="Third Item" /> </asp:ListBox> 

Here, ListBox contains three ListItem controls. When the ListBox control is displayed, the Text property for each ListItem control is displayed within the ListBox control.

You can create controls similar to the ListBox control. You simply need to declare two controls and embed the first control within the opening and closing tags of the second control.

Let's start with some code that doesn't quite work. I'll explain in a moment why the code doesn't work, but first take a look at Listing 28.11.

Listing 28.11 SimpleRotator1.vb
 Imports System Imports System.Web Imports System.Web.UI Namespace myControls Public Class SimpleRotator1: Inherits Control Protected Overrides Sub Render( objTextWriter As HtmlTextWriter)   Dim objRandom As New Random   Dim intRanIndex As Integer   Dim objSelectedItem As RotatorItem1   If Controls.Count > 0 Then     intRanIndex = objRandom.Next( Controls.Count )     objSelectedItem = CType( Controls( intRanIndex ), RotatorItem1 )     objTextWriter.Write( objSelectedItem.Text )   End If End Sub End Class Public Class RotatorItem1 : Inherits Control  Public Text As String End Class End Namespace 

The C# version of this code can be found on the CD-ROM.

In Listing 28.11, two controls are declared. The first control is named SimpleRotator1 , and the second control is named RotatorItem1 .

When the SimpleRotator1 control is rendered, one of the controls in its Controls collection is randomly selected. The value of the Text property of the selected control is outputted.

Listing 28.12 illustrates how the controls are intended to be used in an ASP.NET page.

Listing 28.12 DisplaySimpleRotator1.aspx
 <%@ Register TagPrefix="myControls" Namespace="myControls" Assembly="SimpleRotator1"%> <html> <head><title>DisplaySimpleRotator1.aspx</title></head> <body> <myControls:SimpleRotator1   Runat="Server">   <myControls:RotatorItem1     Text="First Item"     Runat="Server" />   <myControls:RotatorItem1     Text="Second Item"     Runat="Server" />   <myControls:RotatorItem1     Text="Third Item"     Runat="Server" /> </myControls:SimpleRotator1> </body> </html> 

The C# version of this code can be found on the CD-ROM.

In Listing 28.12, an instance of the SimpleRotator1 control is declared; it contains three instances of the RotatorItem1 control. In theory, every time the page is loaded, one of the RotatorItem1 controls should be selected and the value of its Text property should be displayed.

If you attempt to request the page in Listing 28.12; however, you receive an InvalidCastException error slightly more than 50% of the time. What causes this error?

The problem is that the SimpleRotator1 control in Listing 28.12 contains seven controls instead of three. Three of the controls are RotatorItem1 controls. The other four controls are LiteralControl controls.

In Listing 28.12, a newline character separates each RotatorItem1 control. The additional controls are LiteralControl controls that represent these newline characters around each of the RotatorItem1 controls. One LiteralControl appears before the first RotatorItem1 control, and an additional LiteralControl appears after each RotatorItem1 control, which results in a sum total of four unexpected extra controls.

When the Render method executes, it randomly selects a control from the Controls collection and attempts to convert it into a RotatorItem1 control. Because slightly more than half of the controls are not RotatorItem1 controls, this causes an InvalidCastException error slightly more than half the time you request the page.

You need to get the ContentRotator control to ignore anything but RotatorItem1_ controls in its Controls collection. Fortunately, you can do so easily. Take a look at Listing 28.13.

Listing 28.13 SimpleRotator2.vb
 Imports System Imports System.Web Imports System.Web.UI Imports System.Collections Namespace myControls Public Class SimpleRotator2: Inherits Control Public Items As New ArrayList() Protected Overrides Sub AddParsedSubObject( obj As Object )   If ( TypeOf Obj Is RotatorItem2 )     Items.Add( Obj )   End If End Sub Protected Overrides Sub Render(objTextWriter As HtmlTextWriter)   Dim objRandom As New Random   Dim intRanIndex As Integer   Dim objSelectedItem As RotatorItem2   If Items.Count > 0 Then     intRanIndex = objRandom.Next( Items.Count )     objSelectedItem = CType( Items( intRanIndex ), RotatorItem2 )     objTextWriter.Write( objSelectedItem.Text )   End If End Sub End Class Public Class RotatorItem2 : Inherits Control  Public Text As String End Class End Namespace 

The C# version of this code can be found on the CD-ROM.

In Listing 28.13, you make some important changes to the original SimpleRotator control. The first thing you should notice is the new subroutine named AddParsedSubObject . This subroutine is called every time a new object is added to the Controls collection.

You override the AddParsedSubObject method to capture only RotatorItem2 objects and exclude LiteralControl objects. If you capture a RotatorItem2 object, you add it to a collection named Items . Otherwise, you ignore it.

Finally, you modify the Render method so that it randomly selects an object from the Items collection, rather than from the Controls collection. Because you exclude any LiteralControl controls from the Items collection, the Render method no longer produces an error.

After the SimpleRotator2.vb file is compiled and copied to the application's /bin directory, you can use the control in the ASP.NET page in Listing 28.14.

Listing 28.14 DisplaySimpleRotator2.aspx
 <%@ Register TagPrefix="myControls" Namespace="myControls" Assembly="SimpleRotator2"%> <html> <head><title>DisplaySimpleRotator2.aspx</title></head> <body> <myControls:SimpleRotator2   Runat="Server">   <myControls:RotatorItem2     Text="First Item"     Runat="Server" />   <myControls:RotatorItem2     Text="Second Item"     Runat="Server" />   <myControls:RotatorItem2     Text="Third Item"     Runat="Server" /> </myControls:SimpleRotator2> </body> </html> 

The C# version of this code can be found on the CD-ROM.

Creating a Custom Control Builder

In the preceding section, you created a control that enables you to randomly display content in an ASP.NET page. The control works perfectly fine, but it does require some additional unnecessary typing. You need to declare the control like this:

 
 <myControls:SimpleRotator2   Runat="Server">   <myControls:RotatorItem2     Text="First Item"     Runat="Server" />   <myControls:RotatorItem2     Text="Second Item"     Runat="Server" />   <myControls:RotatorItem2     Text="Third Item"     Runat="Server" /> </myControls:SimpleRotator2> 

For each RotatorItem2 control, you need to include both the opening myControls tag prefix and the Runat="Server" attribute. This seems like a lot of extra typing for no apparent reason.

You can modify how the inner content of a control is parsed by creating a custom Control Builder, as illustrated in Listing 28.15. In particular, you can map particular tags to particular types with a Control Builder.

Listing 28.15 SimpleRotator3.vb
[View full width]
 Imports System Imports System.Web Imports System.Web.UI Imports System.Collections Namespace myControls ' Declare Custom Control Builder Public Class SimpleRotator3ControlBuilder : Inherits ControlBuilder   Public Overrides Function GetChildControlType( TagName As String, Attributes As graphics/ccc.gif IDictionary ) As Type     If TagName = "RotatorItem3"       Return GetType( RotatorItem3 )     End If     Return Nothing   End Function End Class ' Declare SimpleRotator <ControlBuilderAttribute(GetType( SimpleRotator3ControlBuilder))> _ Public Class SimpleRotator3: Inherits Control   Public Items As New ArrayList()   ' Exclude Everything but RotatorItem controls   Protected Overrides Sub AddParsedSubObject( Obj As Object )     If ( TypeOf Obj Is RotatorItem3 )       Items.Add(Obj)     End If   End Sub   Protected Overrides Sub Render( objTextWriter As HtmlTextWriter)     Dim objRandom As New Random     Dim intRanIndex As Integer     Dim objSelectedItem As RotatorItem3     If Items.Count > 0 Then       intRanIndex = objRandom.Next( Items.Count )       objSelectedItem = CType( Items( intRanIndex ), RotatorItem3 )       objTextWriter.Write( objSelectedItem.Text )     End If   End Sub End Class ' Declare Rotator Item Public Class RotatorItem3 : Inherits Control  Public Text As String End Class End Namespace 

The C# version of this code can be found on the CD-ROM.

In Listing 28.15, you create a new class named SimpleRotator3ControlBuilder . The SimpleRotator3ControlBuilder class inherits from the .NET ControlBuilder class.

Within the SimpleRotator3ControlBuilder class, you override the GetChildControlType method and map the tag RotatorItem3 to the type RotatorItem3 . Because you map the abbreviated tag to the type, you no longer need to fully declare the RotatorItem controls when using the SimpleRotator control.

Finally, notice that you add a custom attribute to the declaration of the SimpleRotator3 class. You declare the class like this:

 
 <ControlBuilderAttribute(GetType( SimpleRotator3ControlBuilder))> _ Public Class SimpleRotator3: Inherits Control 

The ControlBuilderAttribute indicates that the SimpleRotator3ControlBuilder class should be used as the Control Builder for the SimpleRotator3 control, rather than the default Control Builder. In other words, this attribute associates the SimpleRotator3ControlBuilder class with the SimpleRotator3 control.

After you make these modifications to SimpleRotator ”and compile and copy the control to the application's /bin directory ”you can use simplified syntax when declaring the control. The ASP.NET page in Listing 28.16 illustrates how you can declare the new control.

Listing 28.16 DisplaySimpleRotator3.aspx
 <%@ Register TagPrefix="myControls" Namespace="myControls" Assembly="SimpleRotator3"%> <html> <head><title>DisplaySimpleRotator3.aspx</title></head> <body> <myControls:SimpleRotator3   Runat="Server">   <RotatorItem3 Text="First Item" />   <RotatorItem3 Text="Second Item" />   <RotatorItem3 Text="Third Item" /> </myControls:SimpleRotator3> </body> </html> 

The C# version of this code can be found on the CD-ROM.

In Listing 28.16, notice that you can declare each RotatorItem without using the myControls tag prefix or the Runat="Server" attribute.



ASP.NET Unleashed
ASP.NET 4 Unleashed
ISBN: 0672331128
EAN: 2147483647
Year: 2003
Pages: 263

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