7.5 Templates


Templates provide a mechanism for separating the appearance of a control from the core data binding functionality of the control. A templated control manages the data and (typically) the layout of that data, but it leaves the rendering of the data to a user -provided template, giving you much more flexibility in how a data-bound control displays its contents. Conceptually, you can think of templated controls as providing a place for you to "plug in" whatever output you want displayed for each row in the data source during data binding. Figure 7-12 shows this conceptual model. Note that the templated data-bound control manages the data source and the process of data binding, but leaves the rendering of each row to a "user-defined template." Inside the user-defined template, the user of the control specifies what should be output for each row that is bound.

Figure 7-12. Conceptual Model of Templated Controls

graphics/07fig12.gif

ASP.NET provides three templated data-bound controls: the Repeater , the DataList , and the DataGrid . Each of these controls provides varying degrees of flexibility in layout, and support for different sets of templates, as we will see.

7.5.1 Data Binding Evaluation Syntax

To be able to define the rendering portion of a data-bound control, we need a mechanism to access the currently bound row. ASP.NET defines a special syntax using the " <%# %> " notation for template-based data binding. It looks somewhat similar to traditional ASP evaluation syntax but is evaluated differently. Instead of being evaluated during page rendering, data binding evaluation expressions are evaluated during data binding. And typically, if the expression occurs within the item template of a data-bound control, it is evaluated once per row of the data source during binding.

As a concrete example of using this syntax, Listing 7-11 shows a sample use of the Repeater template control, the most generic of the templated controls. In this example, the Repeater control consists of an ItemTemplate containing two data binding expressions and some static HTML. The data binding expressions are evaluated once per row of the underlying data source ”in this case, an SqlDataReader . Figure 7-13 shows the rendering of this page.

Listing 7-11 Sample Use of Data Binding Evaluation Syntax
 <! File: SimpleRepeater.aspx> <%@ Page language=C# %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <script language="C#" runat="server"> protected void Page_Load(Object src, EventArgs e) {   if (!IsPostBack)   {     SqlConnection conn =     new SqlConnection("server=.;uid=sa;pwd=;database=Test");     SqlCommand cmd =        new SqlCommand("SELECT Name, Age FROM Employees",                       conn);     try     {       conn.Open();       SqlDataReader reader = cmd.ExecuteReader();       rp1.DataSource = reader;       DataBind();     }     finally     { conn.Dispose(); }   } } </script> <body> <form runat=server> <asp:Repeater id="rp1" runat=server>   <ItemTemplate>     <%# ((IDataRecord)Container.DataItem)["Name"] %><br>     <%# ((IDataRecord)Container.DataItem)["Age"] %><br>   </ItemTemplate> </asp:Repeater> </form> </body> </html> 
Figure 7-13. Simple Repeater Page Rendering

graphics/07fig13.gif

To understand what types of expressions can go into a data binding expression within a templated control, we must understand where the expression is going to end up in the generated Page -derived class definition for this .aspx file. Typically, each data binding expression that occurs within a template turns into an instance of the DataBoundLiteralControl class and is added as a child control to the templated control (the Repeater in our example). In addition, the page parser generates a handler function that is wired up to the DataBinding event of the DataBoundLiteralControl class. It is within this handler function that the contents of the data binding expression are inserted, as shown in Figure 7-14.

Figure 7-14. Code Generation for Data Binding Expression within a Template

graphics/07fig14.gif

The DataBinding event of the DataBoundLiteralControl class is triggered once for each row of the data source during the DataBind() call to the Repeater control. The generated code for this handler also prepares a local variable named Container , which is set to the BindingContainer property of the control that issued the event. In our example, the control that issues the event is our DataBoundLiteralControl , and the BindingContainer property returns a reference to the current template, the ItemTemplate , which for the Repeater class is an instance of the RepeaterItem class. Finally, to access the current row in the underlying data source, the RepeaterItem class exposes a DataItem property. Because we chose to bind our Repeater control to an SqlDataReader , the underlying data source exposes the IDataRecord interface, which we can then use to access the current value of the "Name" column.

If, instead of adding data binding expressions to the top-level of an ItemTemplate , you assign the property of a server-side control to a data binding expression, the code generation changes slightly. Listing 7-12 shows a Repeater control that sets the Text property of a server-side TextBox control to a data binding expression.

Listing 7-12 Data Binding Expressions as Property Values
 <asp:Repeater id="rp1" runat=server>   <ItemTemplate>     <asp:TextBox id=_name      Text='<%# ((IDataRecord)Container.DataItem)["Name"] %>'     />   </ItemTemplate> </asp:Repeater> 

In this case, instead of turning the data binding expression into an instance of the DataBoundLiteralControl class, it generates a handler for the DataBinding event of the TextBox control and places the contents of the data binding expression on the right-hand side of an assignment to the Text property of the TextBox control. The end result is the same, but the creation of the DataBoundLiteralControl control is unnecessary.

7.5.2 DataBinder

In the previous example of using the data binding evaluation syntax within a template, the DataItem property of the implicit Container variable was cast to an IDataRecord and then indexed to retrieve the value of a named column for the current row. If, instead of using an SqlDataReader as a data source, we had used a DataSet , this cast would have failed, because rows of a DataSet are not exposed with the IDataRecord interface. We would instead have to cast the Container.DataItem to a DataRowView , which we could then index with our desired column name or index. This means that changing the data source to which your templated controls are bound would involve changing the data binding expressions as well.

Fortunately, another solution works regardless of the type of data source ”it is called the DataBinder class. This class defines a static method called Eval() that evaluates a generic DataItem by first using reflection to determine what type of data source it is and then dynamically constructing a call to its default indexer to retrieve the desired value. This method supports two overloads, the second of which includes a format string that should be applied when retrieving the data. Although this mechanism incurs slightly more overhead, it ensures that any future changes to the type of data source used for binding will work with the existing data binding expressions. Listing 7-13 shows our earlier Repeater example rewritten to use the DataBinder class instead of direct casting. Note that in the second usage, a format string of " {0:2d} " is applied to the requested string, indicating that the resulting value should be displayed as a two-digit decimal value.

Listing 7-13 Using DataBinder.Eval to Create Data-Source-Independent Expressions
 <asp:Repeater id="rp1" runat=server>   <ItemTemplate>   <%# DataBinder.Eval(Container.DataItem, "Name") %><br>   <%# DataBinder.Eval(Container.DataItem, "Age","{0:2d"}) %>   <br>   </ItemTemplate> </asp:Repeater> 

7.5.3 Templated Controls

Now that we have covered the mechanism of data binding in templates, it is time to turn to the templated controls provided by ASP.NET. The three templated controls available are the Repeater , DataList , and DataGrid . Each of these controls supports a different set of templates, beginning with the most common ItemTemplate . Table 7-1 shows the various templates that are available in each of the three classes.

Table 7-1. Available Templates and the Controls That Support Them

Template

Description

DataGrid

DataList

Repeater

ItemTemplate

Generates appearance of items

Yes

Yes

Yes

AlternatingItemTemplate

Generates appearance of every other row item

No

Yes

Yes

HeaderTemplate

Generates appearance of header for columns

Yes

Yes

Yes

FooterTemplate

Generates appearance of footer for columns

Yes

Yes

Yes

SeparatorTemplate

Generates appearance between items

Yes

No

Yes

EditItemTemplate

Generates appearance of item currently being edited

Yes

Yes

No

For an example of applying templates, consider the editable DataGrid presented in the last section. The last column shown in the DataGrid was a Boolean field called Contract , which when displayed in the grid, showed up as the string "true" or "false". Instead of displaying a string in that column, it makes more sense to show a CheckBox that is either checked or not based on the value of the underlying column in the data table. Figure 7-15 shows a new version of our DataGrid , running with a CheckBox instead of a string.

Figure 7-15. Use of a DataGrid Control with a Template Column

graphics/07fig15.gif

To place a CheckBox in a column of a DataGrid , first we need to turn off AutoGenerateColumns so that we control the column creation completely. Then, in place of the Contract column, we define a TemplateColumn consisting of an ItemTemplate , the content of which is a CheckBox control. We then use the declarative data binding syntax described earlier to extract the Boolean value from the Contract column in the underlying data row. Listing 7-14 shows the modified DataGrid declaration (excluding styles) that generates the page shown in Figure 7-15.

Listing 7-14 DataGrid declaration with template column
 <! File: DataGridEditTemplate.aspx > <asp:datagrid id="_gd1" runat=server   AutoGenerateColumns="false"   OnEditCommand="gd1_Edit"   OnCancelCommand="gd1_Cancel"   OnUpdateCommand="gd1_Update">   <Columns>     <asp:EditCommandColumn EditText="Edit"                            CancelText="Cancel"                            UpdateText="Update"                            ItemStyle-Wrap="false"/>   <asp:BoundColumn HeaderText="Id"                    ReadOnly="true" DataField="au_id"/>   <asp:BoundColumn HeaderText="LastName"                    DataField="au_lname"/>   <asp:BoundColumn HeaderText="FirstName"                    DataField="au_fname"/>   <asp:BoundColumn HeaderText="Phone" DataField="phone"/>   <asp:BoundColumn HeaderText="Address"                    DataField="address"/>   <asp:BoundColumn HeaderText="City" DataField="city"/>   <asp:BoundColumn HeaderText="State" DataField="state"/>   <asp:BoundColumn HeaderText="Zip" DataField="zip"/>   <asp:TemplateColumn HeaderText="Contract">     <ItemTemplate>       <asp:CheckBox id="contract" runat=server                     Checked=   '<%# DataBinder.Eval(Container.DataItem, "Contract") %>'       />     </ItemTemplate>   </asp:TemplateColumn>   </Columns> </asp:datagrid> 

In addition to the DataGrid declaration change, we have to change the UpdateCommand event handler to extract the Boolean value from the embedded CheckBox control. The easiest way to do this is to call the FindControl method of the cell with the ID of the CheckBox , as shown in Listing 7-15.

Listing 7-15 Extracting the Value of a Nested Control in a DataGrid
 // File: DataGridEditTemplate.aspx.cs public void gd1_Update(object sender,                        DataGridCommandEventArgs e) {   // other control extraction values not shown   // Change au_id field access because it is read-only   cmd.Parameters.Add("@vId",    e.Item.Cells[1].Text);   // Find the checkbox control in column 9   CheckBox cb =           (CheckBox)e.Item.Cells[9].FindControl("contract");   cmd.Parameters.Add("@vContract", cb.Checked);   //... } 

7.5.4 Repeater

The Repeater control is a very generic shell that gives you complete control over how data is rendered. It requires that you provide all the HTML layout, formatting, and style tags within the templates of the control. You may want to consider using a DataGrid or DataList before using a Repeater because they will do much more of the work on your behalf . If, however, you have a complex data layout that cannot be represented as a list or a grid, the Repeater will accommodate any formatting you can imagine. Figure 7-16 shows a sample use of the Repeater control.

Figure 7-16. A Repeater Control in Use

graphics/07fig16.gif

7.5.5 DataList

The DataList control provides a simple way to display data from a data source in a repeating list. While similar functionality can be achieved using the DataGrid control, the DataList is simpler to work with and is designed to make displaying lists trivial. At the very least, you must define an ItemTemplate to describe how each item appears. Styles are provided for each of the templates, which make it easy to modify the appearance of any templated item. For example, the HeaderStyle property gives you control over the appearance of items in the header, including border, background color , width, and so on. Another set of properties affects how the list flows on the page. Setting the RepeatLayout to Flow , for example, generates div and span elements instead of a table (which is the default). Setting the RepeatDirection lets you control whether elements flow horizontally or vertically. Setting RepeatColumns gives you control over how many columns are displayed before another row is generated. Figure 7-17 shows an example use of the DataList control.

Figure 7-17. DataList Control Example

graphics/07fig17.gif



Essential ASP.NET With Examples in C#
Essential ASP.NET With Examples in C#
ISBN: 0201760401
EAN: 2147483647
Year: 2003
Pages: 94
Authors: Fritz Onion

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net