Chapter 4 Custom Rendered Controls 91
4. Build the project by selecting Build, Build Solution from the main menu.
5. Add the PalindromeCheckerRenderedControl to the toolbox if it’s not already there.
Visual Studio should add the PalindromeCheckerRenderedControl to the Toolbox. If
not, you can add it manually. Click the right mouse button on the toolbox and select
Choose Item. Use the Browse button to fi nd the CustomControlLib.DLL assembly and
select it. Visual Studio will load the new control in the toolbox.
6. Add a page to use the palindrome checker control. Add a new Web Form to the
ControlORama project and name it UsePalindromeCheckerControls.aspx. Drag the
PalindromeCheckerRenderedControl and drop it on the page. Add a TextBox and a but-
ton so you can add a palindrome to the control and check it.
7. Add a handler for the button. Double-click on the button. Visual Studio will add a han-
dler to the page. In the handler, set the PalindromeCheckerRenderedControl’s text prop-
erty to the TextBox.Text property.
public partial class UsePalindromeCheckerControls : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
this.PalindromeCheckerRenderedControl1.Text = this.TextBox1.Text;
}
}
8. Run the page and test for a palindrome. Palindromes should appear in blue and non-
palindromes in red.
92 Part I Fundamentals
Controls and Events
The PalindromeCheckerRenderedControl shows how to render control content differently de-
pending on the state of the Text property. While that’s a very useful thing in itself, it’s often
helpful to also alert the host page to the fact that a palindrome was found. You can do this
by exposing an event from the control.
ViewState["Text"] = value;
if (this.CheckForPalindrome())
{
if (PalindromeFound != null)
{
PalindromeFound(this, EventArgs.Empty);
}
}
}
}
Notice that the code generated by Visual Studio 2008 stores the property in the con-
trol’s ViewState. That way, the property retains its value between posts. We’ll examine
ViewState more closely later in this chapter.
3. Now wire the event in the host page. Remove the current instance of the
PalindromeCheckerRenderedControl from the page and drop a new instance on the
page. This will refresh the CustomControlLib.DLL assembly so the changes (the new
event) will appear in Visual Studio. Select the PalindromeCheckerRenderedControl on
the page and click the Events button (the little lightning bolt) in the property page in
Visual Studio. Double-click on the text box next to the PalindromeFound event. Visual
Studio will create an event handler for you.
94 Part I Fundamentals
4. Respond to the PalindromeFound event. The example here simply prints some text out
to the browser using Response.Write.
public partial class UsePalindromeCheckerControls : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
HTML. In this case, ASP.NET passes in a normal HtmlTextWriter into the RenderControl method.
However, if you happen to get a request from a lesser browser that understands only HTML 3.2,
ASP.NET passes in an Html32TextWriter. The classes are similar as far as their use and may be in-
terchanged. Html32TextWriter emits certain tags (such as table tags) in HTML 3.2 format, while
HtmlTextWriter emits the same tags in HTML4.0 format. Information within machine.confi g and
the browser capabilities confi guration help ASP.NET fi gure out what kind of HtmlTextWriter to
use. The browser capability information deduced by the ASP.NET runtime may be used for more
than simply selecting the correct HtmlTextWriter. The Request property (available as part of the
HttpContext and the Page) includes a reference to the Browser object. This object includes a
number of fl ags indicating various pieces of information, such as the type of browser making the
request, whether the browser supports scripting, and the name of the platform the browser is
running on. This information comes down as part of the headers included with each request. The
ASP.NET runtime runs the headers against some well-known regular expressions within the con-
fi guration fi les to fi gure out the capabilities. For example, here’s a short listing illustrating how to
fi gure out if the browser making the request supports Frames:
public class TestForFramesControl : Control
{
protected override void RenderContents(HtmlTextWriter output)
{
if (Page.Request.Browser.Frames)
{
output.Write(
"This browser supports Frames");
}
else
{
output.Write("No Frames here");
}
}
}
The HtmlTextWriter class and the enumerations include support to hide all the oddities of
switching between HTML 3.2 and 4.0. Listing 4-5 shows how a table would be rendered using
an HTML 4.0–compliant response. Listing 4-6 shows how a table would be rendered using an
HTML 3.2–compliant response.
LISTING 4-5 HTML 4.0 Rendered Control
<br />
<br />
This is a palindrome: <br>
<b><font>Do geese see god?</font></b><br>
<table width="50%" border="1" style="color:blue;">
<tr>
<td align="left" style="font-size:medium;color:blue;">
A man, a plan, a canal, panama.</td>
</tr>
<tr>
<td align="left" style="font-size:medium;color:blue;">
Do geese see god?</td>
</tr>
LISTING 4-6 HTML 3.2 Rendered Control
<br />
<br />
This is a palindrome: <br>
<b><font>Do geese see god?</font></b><br>
<table width="50%" border="1">
<tr>
<td align="left">
<font color="blue" size="4">A man, a plan, a canal, panama.</font>
</td>
</tr>
<tr>
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections
3. Add an ArrayList to the control to hold the viewed palindromes. Update the Text prop-
erty’s setter to store text in the view state if the text is a palindrome.
public class PalindromeCheckerRenderedControl : WebControl
{
public event EventHandler PalindromeFound; // public event
ArrayList alPalindromes = new ArrayList();
[Bindable(true)]
Chapter 4 Custom Rendered Controls 99
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? String.Empty : s);
}
set
{
ViewState["Text"] = value;
string text = value;
this.alPalindromes =
(ArrayList)this.ViewState["palindromes"];
if (this.alPalindromes == null)
{
output.RenderEndTag(); // </tr>
}
output.RenderEndTag(); // </table>
}
100 Part I Fundamentals
protected override void RenderContents (HtmlTextWriter output)
{
if (this.CheckForPalindrome())
{
output.Write("This is a palindrome: <br>");
output.RenderBeginTag(HtmlTextWriterTag.Font);
output.AddStyleAttribute(HtmlTextWriterStyle.Color, "blue");
output.RenderBeginTag(HtmlTextWriterTag.B);
output.Write(Text);
output.RenderEndTag(); // bold
output.RenderEndTag(); // font
} else {
output.Write("This is NOT a palindrome: <br>");
output.RenderBeginTag(HtmlTextWriterTag.Font);
output.AddStyleAttribute(HtmlTextWriterStyle.Color, "red");
output.RenderBeginTag(HtmlTextWriterTag.B);
output.Write(Text);
output.RenderEndTag(); // bold
output.RenderEndTag(); // font
}
output.Write("<br>");
RenderPalindromesInTable(output);
}
5. Build and run the application. When you surf to the page holding the palindrome
checker, you should see the previously found palindromes appearing in the table:
the bill.
Add a custom control to the toolbox
Show the toolbox if it’s not already showing by selecting View,
Toolbox from the main menu.
Click the right mouse button anywhere in the toolbox.
Select Choose Items from the local menu.
Choose a control from the list
OR
Browse to the assembly containing the control.
T
o
D
o Th
i
s
102 Part I Fundamentals
To Do This
Change the properties of controls
on a page
Make sure the page editor is in Design mode.
Highlight the control whose property you want to change.
Select the property to edit in the Properties window.
Manage events fi red by controls on
a page
Make sure the page editor is in Design mode.
Highlight the control containing the event you want your page to
handle.
Select the event in the event window (you may highlight it by pressing
the lightning bolt button in the Properties window).
Double-click in the combo box next to the event to have Visual Studio
Use both kinds of controls in an application
Recognize when each kind of control is appropriate
The last chapter covered the details of controls that do custom rendering, and this chapter
covers the other kind of control—composite controls. ASP.NET defi nes two broad categories
of composite controls—binary custom controls and user custom controls. Each type of com-
posite control has advantages and disadvantages, which we’ll discuss. First, let’s explore the
primary differences between rendered controls and composite-style controls.
Composite Controls versus Rendered Controls
Recall that custom rendered controls completely form and tailor the HTML going back to the
client via the System.Web.UI.Control.RenderControl method. Custom rendered controls take
over the entire rendering process. With custom rendered controls, you have extraordinary
fl exibility and power over the HTML emitted by your Web site—all the way down to the indi-
vidual control level.
However, with that power and fl exibility also comes the need to keep track of an enormous
amount of detail. For example, if you were to add an input button to a custom rendered
control, you’d need to insert the correct HTML to describe the button within the response
stream heading back to the client. Things get even more diffi cult when you decide to add
more complex controls such as selection controls that may need to track collections of items.
Even though input buttons and selection controls are easy to describe in HTML, we’ve seen
that ASP.NET already includes server-side control classes that render the correct tags. The
standard ASP.NET controls greatly simplify user interface (UI) programming for Web forms.
Composite controls take advantage of these server-side controls that have already been writ-
ten. Composite controls are composed from other controls. To illustrate the utility of compos-
ite controls, imagine you’re working on a number of projects whose login screens require a
similar look and feel. On the one hand, you’ve already seen that it’s fairly easy to build Web
forms in Visual Studio. However, if you run into a situation that requires the same group of
controls to appear together in several instances, it’s pretty tedious to recreate those pages
repeatedly. ASP.NET solves this problem with composite controls.
104 Part I Fundamentals
1. Open the ControlORama project. Highlight the CustomControlLib project in the Solution
Explorer. Click the right mouse button on the project node and select Add, New Item.
Create a new class and name the source fi le PalindromeCheckerCompositeControl.cs. Use
the ASP.NET Server Control template (as you did with the PalindromeCheckerRenderedC
ontrol from Chapter 4).
Chapter 5 Composite Controls 105
2. After Visual Studio creates the code, do the following:
Edit the code to change the derivation from WebControl to CompositeControl.
Deriving from the CompositeControl also adds the INamingContainer interface to
the derivation list. (INamingContainer is useful to help ASP.NET manage unique
IDs for the control’s children.)
Add the PalindromeFound event that the host page may use to listen for palin-
drome detections.
Remove the RenderContents method.
Add four member variables—a TextBox, a Button, a Label, and a LiteralControl.
The code should look something like this when you’re fi nished:
public class PalindromeCheckerCompositeControl :
CompositeControl
{
protected TextBox textboxPalindrome;
protected Button buttonCheckForPalindrome;
protected Label labelForTextBox;
protected LiteralControl literalcontrolPalindromeStatus;
public event EventHandler PalindromeFound;
// RenderContents method removed.
{
if (this.Text != null)
{
String strControlText = this.Text;
String strTextToUpper = null;
strTextToUpper = Text.ToUpper();
strControlText = this.StripNonAlphanumerics(strTextToUpper);
char[] rgcReverse = strControlText.ToCharArray();
Array.Reverse(rgcReverse);
String strReverse = new string(rgcReverse);
if (strControlText == strReverse)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
4. Add an event handler to be applied to the Button (which we’ll install on the page soon).
Because this is a binary control without designer support, you’ll need to add the event
handler using the text wizard (that is, you’ll need to type it by hand).
public void OnCheckPalindrome(Object o, System.EventArgs ea)
{
this.Text = this.textboxPalindrome.Text;
every element on the page (or in this case the control) will be rendered using a server-
side control. If you want any literal text rendered as part of your control, or if you need
HTML markup that isn’t included as a provided ASP.NET control (such as the <br/> ele-
ment), you need to package it in a server-side control. The job of a LiteralControl is to
take the contents (the Text property) and simply render it to the outgoing stream.
The second thing to notice is how the event handler is hooked to the Button using a
delegate. When you use Visual Studio’s designer support, you can usually wire event
handlers up by clicking on a UI element in the designer—at which point Visual Studio
adds the code automatically. However, because there’s no designer support here, the
event hookup needs to be handled manually.
6. Show the palindrome status whenever the Text property is set. Modify the Text prop-
erty to match the following bit of code. The Text property’s setter will check for a pal-
indrome and render the result in the LiteralControl we added in Step 2. It should also
raise the PalindromeFound event.
private String text;
public string Text
{
108 Part I Fundamentals
get
{
return text;
}
set
{
text = value;
if (this.CheckForPalindrome())
{
if (PalindromeFound != null)
{
PalindromeFound(this, EventArgs.Empty);
if (this.alPalindromes != null)
Chapter 5 Composite Controls 109
{
foreach (string s in this.alPalindromes)
{
TableCell tableCell = new TableCell();
tableCell.BorderStyle = BorderStyle.Double;
tableCell.BorderWidth = 3;
tableCell.Text = s;
TableRow tableRow = new TableRow();
tableRow.Cells.Add(tableCell);
this.tablePalindromes.Rows.Add(tableRow);
}
}
}
9. Update the Text property’s setter to manage the table. Add palindromes to the ArrayList
as they’re found, and build the palindrome table each time the text is changed.
public string Text
{
get
{
return text;
}
set
{
text = value;
this.alPalindromes = (ArrayList)this.ViewState["palindromes"];
if (this.alPalindromes == null)
{
this.alPalindromes = new ArrayList();
control directly from the toolbox and drop it onto the page. When you run the page, it
will check for palindromes and keep a record of the palindromes that have been found,
like so (tracing is turned on in this example so we can see the control tree later). Note
that this example extends the previous chapter and the page includes the controls
added from the previous chapter:
With tracing turned on, you can look further down and see the control tree. Notice how the
PalindromeCheckerCompositeControl acts as a main node on the tree and that the compos-
ite control’s child controls are shown under the PalindromeCheckerCompositeControl
node.
Chapter 5 Composite Controls 111
When you type palindromes and click the button, the control will detect them. The control
displays the current Text property in red if it’s not a palindrome and in blue if it is a palin-
drome. You can also see the table rendering, showing the currently found palindromes.
112 Part I Fundamentals
The palindrome checker is a good example of a binary composite control. The composite
control lives entirely within the CustomControlLib assembly and does not have any designer
support at present (we could add code to support high-quality design time support, but
that’s beyond the scope of this chapter). Here’s an alternative to coding a composite control
entirely by hand—the second way to create composite controls is via a User control.
User Controls
User controls are composite controls that contain child controls very much like binary com-
posite controls do. However, instead of deriving from System.Web.UI.CompositeControl, they
derive from System.Web.UI.UserControl. Perhaps a better description is that they’re very much
like miniature Web forms. The have a UI component (an .ascx fi le) that works with the Visual
Studio designer, and they employ a matching class to manage the execution. However, unlike
a Web form, they may be dragged onto the toolbox and then dropped into a Web form.
To get a good idea as to how Web User controls work, here’s how to build the palindrome
{
i++;
}
else
114 Part I Fundamentals
{
strStripped = strStripped.Remove(i, 1);
}
}
}
return strStripped;
}
protected bool CheckForPalindrome()
{
if (this.Text != null)
{
String strControlText = this.Text;
String strTextToUpper = null;
strTextToUpper = Text.ToUpper();
strControlText = this.StripNonAlphanumerics(strTextToUpper);
char[] rgcReverse = strControlText.ToCharArray();
Array.Reverse(rgcReverse);
String strReverse = new string(rgcReverse);
if (strControlText == strReverse)
{
return true;
}
else
{
return false;
PalindromeFound(this, EventArgs.Empty);
}
this.labelPalindromeStatus.Text =
String.Format(
"This is a palindrome <br/><FONT size=\"5\" color=\"blue\"><B>{0}</B></FONT>",
text);
}
else
{
this.labelPalindromeStatus.Text =
String.Format(
"This is NOT a palindrome <br/><FONT size=\"5\" color=\"red\"><B>{0}</B></FONT>",
text);
}
}
}
6. Now add support for keeping track of palindromes. Add an ArrayList to the control class.
ArrayList alPalindromes;
7. Add a Table to the control. Switch to the PalindromeCheckerUserControl Design view
and drag a Table onto the form.
8. Add a method to build the table of palindromes. It’s very much like the one in the
PalindromeCheckerCompositeControl, except the name of the table has changed. Table1
is the name given the table by Visual Studio.
protected void BuildPalindromesTable()
{
this.alPalindromes = (ArrayList)this.ViewState["palindromes"];
if (this.alPalindromes != null)
{
foreach (string s in this.alPalindromes)
{