The control in Listing 36.6 renders a drop shadow behind whatever text you assign to the control’s Text property see Figure 36.3.. Furthermore, the control in Listing 36.7 overrides the
Trang 1The control in Listing 36.6 renders a drop shadow behind whatever text you assign to the
control’s Text property (see Figure 36.3) The drop shadow is created with the help of an
Internet Explorer DropShadow filter
The Filter attribute is added to the <div> tag with a call to the AddStyleAttribute()
method The AddStyleAttribute() method works just like the AddAttribute() method,
except that the AddStyleAttribute() method adds a CSS attribute instead of an HTML
attribute
WEB STANDARDS NOTE
Filters are an Internet Explorer extension to the CSS standard They don’t work with
Firefox or Opera Firefox has its own extensions to CSS with its -moz style rules
Specifying the Containing WebControl Tag
By default, a WebControl renders an HTML <span> tag around its contents You can specify
a different tag by overriding the WebControl’s TagKey property For example, the control in
Listing 36.7 renders its contents within an HTML <div> tag
FIGURE 36.3 Displaying a drop shadow with the DropShadow control
Trang 2LISTING 36.7 Glow.cs
using System.Web.UI;
using System.Web.UI.WebControls;
namespace myControls
{
public class Glow : WebControl
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
protected override HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Div;
}
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
writer.AddStyleAttribute(
HtmlTextWriterStyle.Filter, “glow(Color=#ffd700,Strength=10)”);
base.AddAttributesToRender(writer);
}
protected override void RenderContents(HtmlTextWriter writer)
{
writer.Write(_Text);
}
public Glow()
{
this.Width = Unit.Parse(“500px”);
}
}
}
Trang 3The control in Listing 36.7 displays a glowing effect around any text that you assign to its
Text property The control takes advantage of the Internet Explorer Glow filter to create
the glow effect (see Figure 36.4) In a real production application, you would want to
either provide an alternate rendering for other browsers, or stick to more CSS-compliant
rendering
The control overrides the base WebControl’s TagKey property Because the overridden
prop-erty returns a <div> tag, the WebControl renders a <div> tag
NOTE
You can use several methods to modify the tag rendered by a WebControl You can
override the TagName property instead of the TagKey property The TagName property
enables you to specify an arbitrary string for the tag (It doesn’t limit you to the
HtmlTextWriterTag enumeration.) You also can specify the tag rendered by a
WebControl in the WebControl’s constructor Finally, you can override a WebControl’s
RenderBeginTag() and RenderEndTag() methods and completely customize the
open-ing and closopen-ing tags
Furthermore, the control in Listing 36.7 overrides the AddAttributesToRender() method
If you override this method, you can add HTML or CSS attributes to the opening HTML
FIGURE 36.4 Displaying glowing text with the Glow control
Trang 4tag rendered by the control When overriding this method, be careful to call the base
AddAttributesToRender() method or the standard control attributes, such as the control
ID, won’t be rendered
Building Composite Controls
If you don’t want to start from scratch when building a custom control, you can build a
composite control When you create a composite control, you create a new control from
existing controls
Every ASP.NET control has a Controls property that represents all its child controls If you
add child controls to a control, the child controls are automatically rendered when the
parent control is rendered
When you create a composite control, you typically override a control’s
CreateChildControls() method This method is called when a control builds its
collection of child controls
For example, the control in Listing 36.8 combines a TextBox control and
RequiredFieldValidator control
LISTING 36.8 RequiredTextBox.cs
using System;
using System.Web.UI.WebControls;
namespace myControls
{
public class RequiredTextBox : CompositeControl
{
private TextBox input;
private RequiredFieldValidator validator;
public string Text
{
get
{
EnsureChildControls();
return input.Text;
}
set
{
EnsureChildControls();
input.Text = value;
}
}
Trang 5protected override void CreateChildControls()
{
input = new TextBox();
input.ID = “input”;
this.Controls.Add(input);
validator = new RequiredFieldValidator();
validator.ID = “valInput”;
validator.ControlToValidate = input.ID;
validator.ErrorMessage = “(Required)”;
validator.Display = ValidatorDisplay.Dynamic;
this.Controls.Add(validator);
}
}
}
The control in Listing 36.8 inherits from the base CompositeControl class Furthermore,
rather than override the base control’s RenderContents() method, the control overrides
the base control’s CreateChildControls() method
You should notice one other special thing in Listing 36.8 The EnsureChildControls()
method is called in both the Get and Set methods of the Text property The
EnsureChildControls() method forces the CreateChildControls() method to be called;
however, it prevents the CreateChildControls() method from being called more than
once
The Text property gets or sets a property of a child control (the TextBox control) If you
attempt to use the Text property before the CreateChildControls() method is called, you
receive a null reference exception The child controls must be created before you can
access any of the child control properties
The page in Listing 36.9 illustrates how you can use the RequiredTextBox control in a
page
LISTING 36.9 ShowRequiredTextBox.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”>
<script runat=”server”>
protected void btnSubmit_Click(object sender, EventArgs e)
{
Trang 6lblResults.Text = txtUserName.Text;
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show RequiredTextBox</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Label
ID=”lblUserName”
Text=”User Name:”
AssociatedControlID=”txtUserName”
Runat=”server” />
<custom:RequiredTextBox
ID=”txtUserName”
Runat=”Server” />
<br />
<asp:Button
ID=”btnSubmit”
Text=”Submit”
Runat=”server” OnClick=”btnSubmit_Click” />
<hr />
<asp:Label
id=”lblResults”
Runat=”server” />
</div>
</form>
</body>
</html>
The page in Listing 36.9 has tracing enabled If you look at the control tree for the page,
you see that the RequiredTextBox control includes both a TextBox and
RequiredFieldValidator control as child controls
Trang 7Building Hybrid Controls
In practice, you rarely build pure composite controls In most cases in which you override
a control’s CreateChildControls() method, you also override the control’s
RenderContents() method to specify the layout of the child controls
For example, the control in Listing 36.10 represents a Login control In the control’s
CreateChildControls() method, two TextBox controls are added to the control’s
collec-tion of child controls
LISTING 36.10 Login.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace myControls
{
public class Login : CompositeControl
{
private TextBox txtUserName;
private TextBox txtPassword;
public string UserName
{
get
{
EnsureChildControls();
return txtUserName.Text;
}
set
{
EnsureChildControls();
txtUserName.Text = value;
}
}
public string Password
{
get
{
EnsureChildControls();
return txtPassword.Text;
}
Trang 8{
EnsureChildControls();
txtPassword.Text = value;
}
}
protected override void CreateChildControls()
{
txtUserName = new TextBox();
txtUserName.ID = “txtUserName”;
this.Controls.Add(txtUserName);
txtPassword = new TextBox();
txtPassword.ID = “txtPassword”;
txtPassword.TextMode = TextBoxMode.Password;
this.Controls.Add(txtPassword);
}
protected override void RenderContents(HtmlTextWriter writer)
{
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
// Render UserName Label
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.AddAttribute(HtmlTextWriterAttribute.For, txtUserName.ClientID);
writer.RenderBeginTag(HtmlTextWriterTag.Label);
writer.Write(“User Name:”);
writer.RenderEndTag(); // Label
writer.RenderEndTag(); // TD
// Render UserName TextBox
writer.RenderBeginTag(HtmlTextWriterTag.Td);
txtUserName.RenderControl(writer);
writer.RenderEndTag(); // TD
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
// Render Password Label
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.AddAttribute(HtmlTextWriterAttribute.For, txtPassword.ClientID);
writer.RenderBeginTag(HtmlTextWriterTag.Label);
writer.Write(“Password:”);
writer.RenderEndTag(); // Label
writer.RenderEndTag(); // TD
Trang 9// Render Password TextBox
writer.RenderBeginTag(HtmlTextWriterTag.Td);
txtPassword.RenderControl(writer);
writer.RenderEndTag(); // TD
writer.RenderEndTag(); // TR
}
protected override HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Table;
}
}
}
}
In Listing 36.10, the RenderContents() method is overridden to layout the two TextBox
controls The TextBox controls are rendered within an HTML table (see Figure 36.5) Each
TextBox is rendered by calling the RenderControl() method
FIGURE 36.5 Performing layout with an HTML table
Trang 10The default RenderContents() method simply calls the RenderControl() method for each
child control If you override the RenderContents() method, you have more control over
the layout of the control
The Login control in Listing 36.10 uses an HTML table for layout From a web standards
perspective, using HTML tables for layout is frowned upon The modified Login control in
Listing 36.11 uses <div> tags instead of a <table> tag for layout
LISTING 36.11 LoginStandards.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace myControls
{
public class LoginStandards : CompositeControl
{
private TextBox txtUserName;
private TextBox txtPassword;
public string UserName
{
get
{
EnsureChildControls();
return txtUserName.Text;
}
set
{
EnsureChildControls();
txtUserName.Text = value;
}
}
public string Password
{
get
{
EnsureChildControls();
return txtPassword.Text;
}
set
{