That is, a page is a server control that uses other server controls to render its UI.. By default, if you derive a server control from the Control class, nested elements declared within
Trang 1 Allows expando attributes (i.e attributes that are not pre-defined properties of the control) to be specified in a server-control declaration Expando attributes are not interpreted and are written directly to the output HTML stream By default, the Control class throws an exception if an attribute is not a property of the server control
Provides consistency with the standard ASP.NET web controls since they derive from the WebControl class
Persists the style object and any settings/state during postbacks using view state In the control example earlier, when we used the style object, any style changes made in event handlers or in other code would not have been remembered after a postback, since the state of the style object was not being round tripped using viewstate For our control to remember any style changes (because it is derived from Control), we'd need to implement custom statement management, by overriding the LoadViewState and SaveViewState methods Viewstate
is discussed later in the chapter
The WebControl class is designed to either assist with the rendering of a control, or take complete control of the rendering For a simple control such as our label, the WebControl class can be used to replace most of the code we have written
To make use of the WebControl class we have to make the following changes to our code:
Derive from the WebControl class rather than Control class
Declare a public constructor that calls the base constructor, specifying which HTML element to render
Override the RenderContents method to emit the content we want within our <h1> element The
WebControl class takes responsibility for rendering the attributes and the begin/end-tags, so we remove the Render method
Remove all of the style properties we implemented earlier, since the WebControl will automatically have implemented them for us
After making these changes, our C# control code looks like this:
Trang 2public class MyLabel : WebControl
get{ return _text; }
set{ _text = value; }
Trang 3Private _text As String
Public Sub New()
MyBase.New("H1")
End Sub 'New
Trang 4Public Property Text As String
Protected Overrides Sub OnInit(e As EventArgs) MyBase.OnInit(e)
If _text Is Nothing Then
_text = "Here is some default text"
Trang 5With these changes, we need to change our ASP.NET page since the available properties names for our control have changed You can use the WebControl class documentation to get a list of all the available properties
Here is our updated page, which now uses properties consistent with all the server controls provided as part of ASP.NET, which were discussed in Chapter 5:
<%@ Register TagPrefix="Wrox" Namespace="WroxControls"
do not render any UI, in which case the services provided by WebControl do not provide any benefit
You can use all the other techniques we've used to render HTML so far with server controls derived from WebControl, the only difference is that your code is moved to the RenderContents method
In this revised code demonstrating the use of WebControl, I have removed support for the RepeatCount property, since that would require one or more HTML elements to be rendered at the root level (you can have any number of controls within the root element) This is something a basic web control can do If you need to do this, you have to
Trang 6override the Render method
To implement the RepeatCount property in our implementation, the overridden Render method calls the base implementation of Render a number of times The following code just calls the base Render method 3 times to demonstrate the technique:
End Sub 'Render
As expected, the control will render the label three times:
Trang 7To show that our server control can also be programmatically manipulated, just like any other ASP.NET server control provided out of the box, the following ASP.NET page sets all the properties of our server-control properties within the Page_Init event:
<%@ Register TagPrefix="Wrox" Namespace="WroxControls"
Assembly="MyLabel" %>
<%@ Import Namespace="System.Drawing" %>
<script runat="server" language="C#">
void Page_Init( object sender, EventArgs e )
{
ourLabel.Text = "Web Controls";
ourLabel.ForeColor = Color.Yellow;
ourLabel.BackColor = Color.Black;
Trang 8Composite Controls
All ASP.NET dynamic pages are created by the ASP.NET run-time using ASP.NET controls Although you may not have realized it, we have already built several ASP.NET controls in the earlier chapters of this book just by creating ASP.NET pages and user controls The ASP.NET page framework automatically converts and compiles pages into server controls contained within a dynamically created assembly the first time a page is requested:
Trang 9This server control is then used to render the page Subsequently, when a page is requested, the precompiled server
control can just be instantiated and called, resulting in great performance:
Trang 10The assemblies created for ASP.NET pages are automatically managed for you If you search around your Windows
system directory, you'll find a directory called Temporary ASP.NET Files Within this you'll find sub-directories for the
various web sites on your machine, which in turn will contain the assemblies for ASP.NET pages
If we open up one of these generated assemblies using the ILDASM tool, we'll see that they typically contain a single class
with the same name as the ASP.NET page This class extends (derives from) the class System.Web.UI.Page: (see the
sixth item from the root in the tree control.)
Trang 11The Page object (located in the assembly System.Web.dll) derives from the TemplateControl class, which in turn
derives from the Control class This diagram shows the main ASP.NET control classes and their inheritance hierarchy:
Here is a brief description of the role of each of these classes:
Control - provides a common base class for all other control types.
Trang 12 WebControl - provides methods and properties for dealing with the style This class is the base class for all ASP web controls These are always declared in an ASP.NET page using an ASP: prefix
HtmlControl - base class for standard HTML elements, such as input Typically you will never derive from this control
TemplateControl - contains functionality that is shared between user controls and pages, such as support for
loading user controls (.ascx) or templates
UserControl - base class from which all user controls derive
Page - base class from which all dynamically compiled ASP.NET pages derive
The actual code generated by ASP.NET for these dynamically generated classes is not that different from the code we have just written for our label control One key difference is that it uses control composition to actually generate the page That
is, a page is a server control that uses other server controls to render its UI
Building a Composite Control
A server control can be a container for other controls The Control class has a Controls property of the type ControlsCollection This collection class can hold zero or more child controls When a control is rendered, each of its child controls is called upon to render itself If these child controls also contain child controls, this process repeats, until all controls have been rendered
By default, if you derive a server control from the Control class, nested elements declared within an ASP.NET page will
be added to the Controls collection, assuming you haven't used the ParseChildren attribute discussed earlier to change the default behavior
Assuming we had a server control with a class definition that basically did nothing:
Trang 13{
}
};
and then declared that control with nested sub-controls (a button and some literal text) on a page:
<%@ Register TagPrefix="Wrox" Namespace="WroxControls"
Trang 14The control would actually render the button and the literal text, since the default Render implementation invokes the Render method of each control in the Controls collection:
When writing a composite control, the control itself typically decides on what child controls to create, rather than the user
If the user is going to create all the UI, you should use a User control rather than a custom server control
Server controls override the CreateChildControls method and populate the Controls collection The ASP.NET framework calls this method to signal to a control that it should create its child controls What controls you populate the controls collection with depend on the UI your control renders Any class can be used as long as it derives from the Control class
The code required to implement our first server control using control composition is shown here:
Trang 15By removing the Render method from our class, the default implementation of the Render method defined in the Control class is called during the render phase of a page The default Render method enumerates the Controls collection and invokes the RenderControl method of each child control in turn This effectively causes a control's UI to
be drawn, by allowing each child control to output their own HTML, by either using the HtmlTextWriter object, or, again,
by also using child controls
When deriving from the WebControl class and overriding the RenderContents methods, you should always call the base implementation of RenderContents if your WebControl uses control composition This is because it's the method that calls its base classes' (the Control class) Render method The same is true if you derive from Control and override Control.Render (assuming you want child controls declared in a page to be rendered)
Trang 16associated with a given control
As a control author you should always use the EnsureChildControls method if you need to populate your own controls collection prematurely This method calls CreateChildControls only if the public property ChildControlsCreated
is false If the property is false, it is set to true once CreateChildControls is called This method ensures child controls are only ever created once
If the ChildControlsCreated property is set to false in your code, all child controls are released automatically Subsequently, CreateChildControls may be called again
ASP.NET Pages and Composite Controls
An ASP.NET page is essentially compiled into a composite control Consider this simple page:
<html>
<body>
<form method="post" runat="server">
Name: <asp:textbox runat="server"/>
Trang 17The Page object's Controls collection will contain the child controls LiteralControl, HtmlForm, and
LiteralControl The HtmlForm control will contain the child controls LiteralControl, TextBox, and
LiteralControl When the page is rendered, each of these controls will be rendered in turn, starting at the top,
recursively rendering each child control before moving onto the next sibling So, for this page the rendering sequence
would be Page, LiteralControl, HtmlForm, LiteralControl, TextBox, LiteralControl, LiteralControl
Control Tree Navigation
A server control tree can be navigated using different methods of the Control class To navigate down one level you use
the Control.Controls collection To navigate up the tree one level, from a control to its parent, you use the Parent
property To navigate from a Control to the container Page you use the Control object's Page property To recursively
search down the tree for a control you use the Control object's FindControl method
The Advantages of Control Composition
Using control composition for simple controls really doesn't have much advantage over rendering the HTML directly using
HtmlTextWriter To see when there is a much greater benefit, let's create a more complex control This control will
create an HTML table below a <h1> element The table will contain 10 rows, each with 5 cells We could render this using
the HtmlTextWriter or by creating lots of LiteralContent objects, but it makes a lot more sense to use the
high-level ASP.NET web controls we have seen in earlier chapters of this book Although we have typically only made
references to these controls in ASP.NET pages before, creating these controls dynamically within a control is
straightforward:
using System;
using System.Web;
Trang 18Table _table; // Make table a member so we can access it at any point
protected override void CreateChildControls()
{
LiteralControl text;
text = new LiteralControl(
"<h1>ASP.NET Control Development in C#</h1>");
Controls.Add(text);
TableRow row;
TableCell cell;
// Create a table and set a 2-pixel border
_table = new Table();
_table.BorderWidth = 2;
Trang 19Controls.Add(_table);
// Add 10 rows each with 5 cells
for(int x = 0; x < 10; x++) {
// Create a row and add it to the table
row = new TableRow();
_table.Rows.Add(row);
// Create a cell that contains the text
for(int y = 0; y < 5; y++) {
text = new LiteralControl("Row: " + x + " Cell: " + y);
cell = new TableCell();
Trang 20(System.Web.UI.WebControls.Table), sets the border width to two pixels and adds that as a child control:
_table = new Table();
All of the controls we have used (Table, TableRow, TableCell, etc.) derive from the Control class somewhere in their inheritance hierarchy Each of these controls also uses control composition to render its UI The control tree for our page actually looks something like this:
Trang 21If you view the source for this generated page you'll see around 130 lines of HTML We didn't directly create any of that, and we have gained a number of key advantages by using control composition over direct rendering:
We have saved ourselves a great deal of error-prone HTML creation via code, and therefore saved time and increased our productivity
We have programmatically created objects, called methods, and set properties The code we've written is therefore simple to read, and easily extendable at a later date
We have not been exposed to the underlying HTML generated by the various controls we have used
The fact that they actually render table, tr, and td HTML elements to the page is an implementation detail that
we do not have to concern ourselves with In theory, the output could just as easily be WML or any other markup language - our control will still work just fine
The argument for using composite controls becomes more apparent as the child controls we use, such as the Table control, provide more and more functionality - and hence save us more time and effort For example, let's modify our table
so the user can edit each of the cells within it by using a TextBox control within our TableCell control, rather than a LiteralControl:
for( int y=0; y < 5; y++ ) {
TextBox textbox;