ptg 1614 CHAPTER 36 Building Custom Controls You specify advanced postback options by taking advantage of the PostBackOptions class. This class has the following properties: . ActionUrl—Enables you to specify the page where form data is posted. . Argument—Enables you to specify a postback argument. . AutoPostBack—Enables you to add JavaScript necessary for implementing an AutoPostBack event. . ClientSubmit—Enables you to initiate the postback through client-side script. . PerformValidation—Enables you to specify whether validation is performed (set by the CausesValidation property). . RequiresJavaScriptProtocol—Enables you to generate the JavaScript: prefix. . TargetControl—Enables you to specify the control responsible for initiating the postback. . TrackFocus—Enables you to scroll the page back to its current position and return focus to the control after a postback. . ValidationGroup—Enables you to specify the validation group associated with the control. Imagine that you need to create a form that enables users to place a product order. However, imagine that you want to create an advanced options check box. When someone clicks the advanced options check box, the current form data is submitted to a new page that includes a more complex form. The AdvancedCheckBox control in Listing 36.22 supports cross-page posts. When you click the check box, the form data is submitted to the page indicated by its PostBackUrl property. NOTE Cross-page posts are covered during the discussion of Button controls in Chapter 2, “Using the Standard Controls.” LISTING 36.22 AdvancedCheckBox.cs using System; using System.Web.UI; using System.Web.UI.WebControls; namespace myControls { public class AdvancedCheckBox : WebControl { From the Library of Wow! eBook ptg 1615 Processing Postback Data and Events 36 private string _Text; private string _PostBackUrl; public string Text { get { return _Text; } set { _Text = value; } } public string PostBackUrl { get { return _PostBackUrl; } set { _PostBackUrl = value; } } protected override void AddAttributesToRender(HtmlTextWriter writer) { PostBackOptions options = new PostBackOptions(this); options.ActionUrl = _PostBackUrl; string eRef = Page.ClientScript.GetPostBackEventReference(options); writer.AddAttribute(HtmlTextWriterAttribute.Onclick, eRef); writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID); writer.AddAttribute(HtmlTextWriterAttribute.Type, “checkbox”); base.AddAttributesToRender(writer); } protected override void RenderContents(HtmlTextWriter writer) { if (!String.IsNullOrEmpty(_Text)) { writer.AddAttribute(HtmlTextWriterAttribute.For, this.ClientID); writer.RenderBeginTag(HtmlTextWriterTag.Label); writer.Write(_Text); writer.RenderEndTag(); } } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Input; From the Library of Wow! eBook ptg 1616 CHAPTER 36 Building Custom Controls } } } } In the AddAttributesToRender() method in Listing 36.22, an instance of the PostBackOptions class is created. The ActionUrl property is modified to support cross-page posts. The instance of the PostBackOptions class is passed to the GetPostBackEventReference() method to generate the JavaScript for initiating the postback. The page in Listing 36.23 illustrates how you can use the AdvancedCheckBox control to submit form data to a new page when you click the check box (see Figure 36.10). The AdvancedCheckBox control’s PostBackUrl property is set to the value ShowAdvancedOptions.aspx. When you click the check box, the form data is posted to this page. FIGURE 36.10 Using the AdvancedCheckBox control. LISTING 36.23 ShowAdvancedCheckBox.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”custom” Namespace=”myControls” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” From the Library of Wow! eBook ptg 1617 Processing Postback Data and Events 36 “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> public string ProductName { get { return txtProductName.Text; } } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show AdvancedCheckBox</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:Label id=”lblProductName” Text=”Product Name:” AssociatedControlID=”txtProductName” Runat=”server” /> <asp:TextBox id=”txtProductName” Runat=”server” /> <br /><br /> <custom:AdvancedCheckBox id=”AdvancedCheckBox1” Text=”Advanced Options” PostBackUrl=”AdvancedOptions.aspx” Runat=”server” /> </div> </form> </body> </html> From the Library of Wow! eBook ptg 1618 CHAPTER 36 Building Custom Controls Working with Control Property Collections When you build more complex controls, you often need to represent a collection of items. For example, the standard ASP.NET DropDownList control contains one or more ListItem controls that represent individual options in the DropDownList. The GridView control can contain one or more DataBoundField controls that represent particular columns to display. In this section, we build several controls that represent a collection of items. We build multiple content rotator controls that randomly display HTML content, and a server-side tab control that renders a tabbed view of content. Using the ParseChildren Attribute When building a control that contains a collection of child controls, you need to be aware of a ParseChildren attribute, which determines how the content contained in a control is parsed. When the ParseChildren attribute has the value True, content contained in the control is parsed as properties of the containing control. If the control contains child controls, the child controls are parsed as properties of the containing control. (The attribute should have been named the ParseChildrenAsProperties attribute.) When the ParseChildren attribute has the value False, no attempt is made to parse a control’s child controls as properties. The content contained in the control is left alone. The default value of the ParseChildren attribute is False. However, the WebControl class overrides this default value and sets the ParseChildren attribute to the value to True. Therefore, you should assume that ParseChildren is False when used with a control that inherits directly from the System.Web.UI.Control class, but assume that ParseChildren is True when used with a control that inherits from the System.Web.UI.WebControls.WebControl class. Imagine, for example, that you need to create a content rotator control that randomly displays content in a page. There are two ways of creating this control, depending on whether ParseChildren has the value True or False. The control in Listing 36.24 illustrates how you can create a content rotator control when ParseChildren has the value False. LISTING 36.24 ContentRotator.cs using System; using System.Web.UI; using System.Web.UI.WebControls; namespace myControls { From the Library of Wow! eBook ptg 1619 Working with Control Property Collections 36 [ParseChildren(false)] public class ContentRotator : WebControl { protected override void AddParsedSubObject(object obj) { if (obj is Content) base.AddParsedSubObject(obj); } protected override void RenderContents(HtmlTextWriter writer) { Random rnd = new Random(); int index = rnd.Next(this.Controls.Count); this.Controls[index].RenderControl(writer); } } public class Content : Control { } } The file in Listing 36.24 actually contains two controls: ContentRotator and a Content. The ContentRotator control randomly selects a single Content control from its child controls and renders the Content control to the browser. This all happens in the control’s RenderContents() method. The ParseChildren attribute has the value False in Listing 36.24. If you neglected to add this attribute, the Content controls would be parsed as properties of the ContentRotator control, and you would get an exception. NOTE The AddParsedSubObject() method is discussed in the next section. The page in Listing 36.25 illustrates how you can use the ContentRotator and Content controls (see Figure 36.11). From the Library of Wow! eBook ptg 1620 CHAPTER 36 Building Custom Controls LISTING 36.25 ShowContentRotator.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”custom” Namespace=”myControls” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” ➥ “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show ContentRotator</title> </head> <body> <form id=”form1” runat=”server”> <div> <custom:ContentRotator id=”ContentRotator1” Runat=”server”> <custom:Content id=”Content1” Runat=”server”> First Content Item </custom:Content> <custom:Content FIGURE 36.11 Randomly displaying content with the ContentRotator control. From the Library of Wow! eBook ptg 1621 Working with Control Property Collections 36 id=”Content2” Runat=”server”> Second Content Item <asp:Calendar id=”Calendar1” Runat=”server” /> </custom:Content> <custom:Content id=”Content3” Runat=”server”> Third Content Item </custom:Content> </custom:ContentRotator> </div> </form> </body> </html> If ParseChildren is not set to the value False, you need to add a property to your control that corresponds to the child controls contained in the control. For example, the control in Listing 36.26 includes an Items property that represents the Item controls contained in the control. LISTING 36.26 ItemRotator.cs using System; using System.Collections; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; namespace myControls { [ParseChildren(true, “Items”)] public class ItemRotator : CompositeControl { private ArrayList _items = new ArrayList(); [Browsable(false)] public ArrayList Items { get { return _items; } } From the Library of Wow! eBook ptg 1622 CHAPTER 36 Building Custom Controls protected override void CreateChildControls() { Random rnd = new Random(); int index = rnd.Next(_items.Count); Control item = (Control)_items[index]; this.Controls.Add(item); } } public class Item : Control { } } In Listing 36.26, the second value passed to the ParseChildren attribute is the name of a control property. The contents of the ItemRotator are parsed as items of the collection represented by the specified property. Unlike the ContentRotator control, the controls contained in the ItemRotator control are not automatically parsed into child controls. After the CreateChildControls() method executes, the ItemRotator control contains only one child control (the randomly selected Item control). The page in Listing 36.27 illustrates how you can use the ItemRotator control to randomly display page content. LISTING 36.27 ShowItemRotator.aspx <%@ Page Language=”C#” Trace=”true” %> <%@ Register TagPrefix=”custom” Namespace=”myControls” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” ➥ “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show ItemRotator</title> </head> <body> <form id=”form1” runat=”server”> <div> <custom:ItemRotator id=”ItemRotator1” Runat=”server”> <custom:item ID=”Item1” runat=”server”> First Item From the Library of Wow! eBook ptg 1623 Working with Control Property Collections 36 </custom:item> <custom:item ID=”Item2” runat=”server”> Second Item <asp:Calendar id=”Calendar1” Runat=”server” /> </custom:item> <custom:item ID=”Item3” runat=”server”> Third Item </custom:item> </custom:ItemRotator> </div> </form> </body> </html> There is no requirement that the contents of a control must be parsed as controls. When building a control that represents a collection of items, you can also represent the items as objects. For example, the ImageRotator control in Listing 36.28 contains ImageItem objects. The ImageItem class does not represent a control. LISTING 36.28 ImageRotator.cs using System; using System.Collections; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; namespace myControls { [ParseChildren(true, “ImageItems”)] public class ImageRotator : WebControl { private ArrayList _imageItems = new ArrayList(); public ArrayList ImageItems { get { return _imageItems; } } From the Library of Wow! eBook . instance of the PostBackOptions class is created. The ActionUrl property is modified to support cross-page posts. The instance of the PostBackOptions class is passed to the GetPostBackEventReference(). control in Listing 36.22 supports cross-page posts. When you click the check box, the form data is submitted to the page indicated by its PostBackUrl property. NOTE Cross-page posts are covered during. ptg 16 14 CHAPTER 36 Building Custom Controls You specify advanced postback options by taking advantage of the PostBackOptions class. This class has the following properties: . ActionUrl—Enables