If you look in the Control Tree section, you see that the ImageRotator control does not contain any child controls see Figure 36.12.. Using the AddParsedSubObject Method When the ParseCh
Trang 1protected override void RenderContents(HtmlTextWriter writer)
{
if (_imageItems.Count > 0)
{
Random rnd = new Random();
ImageItem img =
➥(ImageItem)_imageItems[rnd.Next(_imageItems.Count)];
writer.AddAttribute(HtmlTextWriterAttribute.Src, img.ImageUrl);
writer.AddAttribute(HtmlTextWriterAttribute.Alt, img.AlternateText);
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag();
}
}
}
public class ImageItem
{
private string _imageUrl;
private string _alternateText;
public string ImageUrl
{
get { return _imageUrl; }
set { _imageUrl = value; }
}
public string AlternateText
{
get { return _alternateText; }
set { _alternateText = value; }
}
}
}
The ImageItem class is just a class and does not derive from the base Control class
Because the ImageItem class does nothing more than represent a couple of properties,
there is no reason to make it a full-blown control
The page in Listing 36.29 illustrates how you can use the ImageRotator control to display
different images randomly
Trang 2LISTING 36.29 ShowImageRotator.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 ImageRotator</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<custom:ImageRotator
id=”ImageRotator1”
Runat=”server”>
<custom:ImageItem ImageUrl=”Image1.gif” AlternateText=”Image 1” />
<custom:ImageItem ImageUrl=”Image2.gif” AlternateText=”Image 2” />
<custom:ImageItem ImageUrl=”Image3.gif” AlternateText=”Image 3” />
</custom:ImageRotator>
</div>
</form>
</body>
</html>
The page in Listing 36.29 has tracing enabled If you look in the Control Tree section, you
see that the ImageRotator control does not contain any child controls (see Figure 36.12)
FIGURE 36.12 The ShowImageRotator.aspx page control tree
Trang 3Using the AddParsedSubObject() Method
When the ParseChildren attribute has the value false, the contents of a control are
auto-matically added to the control’s collection of child controls (represented by the Controls
property) You need understand that all content contained in the control, even carriage
returns and spaces, are added to the controls collection
Any content contained in a control that does not represent a server-side control is parsed
into a Literal control In some cases, you might want to allow only a certain type of
control to be added to the Controls collection
The AddParsedSubObject() method is called as each control is added to the Controls
collection By overriding the AddParsedSubObject() method, you can block certain types
of controls—such as Literal controls—from being added to the Controls collection
For example, the ContentRotator control in Listing 36.20 overrides the base
AddParsedSubObject() method and prevents anything that is not a Content control from
being added to the ContentRotator Controls collection If you removed the
AddParsedSubObject() method from this control, all the carriage returns and spaces
between the Content controls would be added to the Controls collection as Literal
controls
Using a ControlBuilder
The AddParsedSubObject() method enables you to specify which parsed controls get
added to a Controls collection Sometimes, you must take even more control over the
parsing of a control
When the ASP.NET Framework parses a page, the Framework uses a special type of class
called a ControlBuilder class You can modify the way in which the content of a control
is parsed by associating a custom ControlBuilder with a control
Here’s a list of the most useful methods supported by the ControlBuilder class:
a control
control
into a control
The GetChildControlType() method is the most useful method and enables you to map tags
to controls You can use the GetChildControlType() method to map any tag to any control
For example, the file in Listing 36.30 contains a ServerTabs control that renders multiple
tabs (see Figure 36.13) Each tab is represented by a Tab control
Trang 4LISTING 36.30 ServerTabs.cs
using System;
using System.Collections;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace myControls
{
[ControlBuilder(typeof(ServerTabsBuilder))]
[ParseChildren(false)]
public class ServerTabs : WebControl, IPostBackEventHandler
{
public int SelectedTabIndex
{
get
{
if (ViewState[“SelectedTabIndex”] == null) return 0;
else return (int)ViewState[“SelectedTabIndex”];
}
set
FIGURE 36.13 Using the ServerTabs control
Trang 5{
ViewState[“SelectedTabIndex”] = value;
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
for (int i = 0; i < this.Controls.Count; i++)
{
ServerTab tab = (ServerTab)this.Controls[i];
string eRef = Page.ClientScript.GetPostBackClientHyperlink(this, i.ToString());
if (SelectedTabIndex == i) writer.AddAttribute(
HtmlTextWriterAttribute.Class, “tab selectedTab”);
else writer.AddAttribute(HtmlTextWriterAttribute.Class, “tab”);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.AddAttribute(HtmlTextWriterAttribute.Href, eRef);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.Write(tab.Text);
writer.RenderEndTag(); // A writer.RenderEndTag(); // Tab DIV }
writer.Write(“<br style=’clear:both’ />”);
writer.AddAttribute(HtmlTextWriterAttribute.Class, “tabContents”);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
this.Controls[SelectedTabIndex].RenderControl(writer);
writer.RenderEndTag(); // Tab Contents DIV
}
protected override void AddParsedSubObject(object obj)
{
if (obj is ServerTab)
base.AddParsedSubObject(obj);
}
protected override HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Div;
}
}
Trang 6public void RaisePostBackEvent(string eventArgument)
{
SelectedTabIndex = Int32.Parse(eventArgument);
}
}
public class ServerTabsBuilder : ControlBuilder
{
public override Type GetChildControlType(string tagName, IDictionary attribs)
{
if (String.Compare(tagName, “tab”, true) == 0)
return typeof(ServerTab);
else
return null;
}
}
public class ServerTab : Control
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
}
}
The ServerTabs class is decorated with a ControlBuilder attribute This attribute
associ-ates the ServerTabs control with a ControlBuilder class named ServerTabsBuilder
The ServerTabsBuilder class overrides the base ControlBuilder GetChildControlType()
method The overridden method maps the <tab> tag to the Tab control Because of this
mapping, you do not need to use a prefix or use the runat=”server” attribute when
declaring a tab within the ServerTabs control
The page in Listing 36.31 illustrates how you can use the ServerTabs control
LISTING 36.31 ShowServerTabs.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”>
Trang 7<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<style type=”text/css”>
html
{
background-color:silver;
}
.tab
{
float:left;
position:relative;
top:1px;
background-color:#eeeeee;
border:solid 1px black;
padding:0px 15px;
margin-left:5px;
}
.tab a
{
text-decoration:none;
}
.selectedTab
{
background-color:white;
border-bottom:solid 1px white;
}
.tabContents
{
border:solid 1px black;
background-color:white;
padding:10px;
height:200px;
}
</style>
<title>Show ServerTabs</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<custom:ServerTabs
ID=”ServerTabs1”
Runat=”Server”>
<tab Text=”First Tab”>
Contents of the first tab
</tab>
Trang 8<tab Text=”Second Tab”>
Contents of the second tab
</tab>
<tab Text=”Third Tab”>
Contents of the third tab
</tab>
</custom:ServerTabs>
</div>
</form>
</body>
</html>
The ControlBuilder enables you to declare instances of the Tab control by using the
<tab> tag instead of using a <custom:Tab runat=”server”> tab
Creating a Better Designer Experience
Up to this point, we’ve ignored the Design view experience In other words, we’ve ignored
the question of how our custom controls appear in the Visual Web Developer or Visual
Studio NET Design view
You can modify the appearance of your control in Design view in two ways You can apply
design-time attributes to the control, or you can associate a ControlDesigner with your
control We explore both methods in this section
Applying Design-Time Attributes to a Control
Design-time attributes enable you to modify how control properties appear in Design
view Some attributes are applied to the control itself, whereas other attributes are applied
to particular properties of a control
Here is the list of the design-time attributes you can apply to a control:
double-click a control in Visual Web Developer or Visual Studio NET, an event
handler is automatically created for the default event
you open the Property window for a control, this property is highlighted by default
persisted as control attributes or control contents
dragged from the toolbox
Trang 9Here is the list of design-time attributes you can apply to a control property:
property
window
property appears under this category in the Properties window
right-click a property in the Properties window, you can select Reset to the return
the property to its default value
The description appears in the Properties window when the property is selected
prop-erty are serialized Possible values are Visible, Hidden, and Content
view
be propagated to the parent property
attribute or control content Possible values are Attribute,
EncodedInnerDefaultProperty, InnerDefaultProperty, and InnerProperty
type converter converts a property between a string representation and a type (or
vice versa)
The Editor attribute enables you to associate a particular editor with a property Certain
types in the Framework have default editors For example, a property that represents a
System.Drawing.Color value is automatically associated with the ColorEditor The
ColorEditor displays a color picker (see Figure 36.14) To view the list of editors included
in NET Framework, look up the UITypeEditor class in the NET Framework SDK
Documentation
The MovieView control contained in Listing 36.32 illustrates how you can use several of
these attributes The control displays a single movie
Trang 10LISTING 36.32 MovieView.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace myControls
{
[DefaultProperty(“Title”)]
public class MovieView : WebControl
{
private string _title = “Movie Title”;
private string _description = “Movie Description”;
[Category(“Movie”)]
[Description(“Movie Title”)]
public string Title
{
FIGURE 36.14 Using the ColorEditor to pick a color