Data-Bound Controls

Data-Bound Controls

Speaking of data binding: the WebControls namespace includes three controls whose primary mission in life is to bind to data sources and display the results as HTML. The controls are Repeater, DataList, and DataGrid. Repeater controls use UI templates snippets of HTML that define the appearance of the controls output to render items obtained from a data source. DataList controls also use UI templates, but they add support for multicolumn formatting, item selection, and item editing. DataGrid controls display tabular data in highly customizable HTML tables and support paging, sorting, editing, and other features. Repeater, DataList, and DataGrid are arguably the three most powerful members of the Web control family. Let s see why real-world developers consider them to be an indispensable part of their toolbox.

Repeater Controls

Repeater controls provide a flexible and easy-to-use mechanism for displaying repetitive lists of items. A repeater has no default user interface; you tell a Repeater what to display and how to display it. Here s a simple example that uses a Repeater to display a list of text strings stored in an array:

<html> <body> <form runat="server"> <asp:Repeater  RunAt="server"> <ItemTemplate> <%# Container.DataItem %><br> </ItemTemplate> </asp:Repeater> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { string[] beatles = { "John", "Paul", "George", "Ringo" }; MyRepeater.DataSource = beatles; MyRepeater.DataBind (); } } </script>

ItemTemplate is a Repeater property that defines the appearance of individual items. Content bracketed by <ItemTemplate> and </ItemTemplate> tags constitutes an item template that s invoked repeatedly to render all the items in the data source. Statements between <%# and %> symbols are data-binding expressions. Inside a data-binding expression, Container.DataItem represents the item that the control is currently binding to for example, the current row in a DataTable or the current string in a string array.

An item template forms the core of a Repeater control, but Repeater controls support other template types as well. For example, you can render alternating items differently by using alternating item templates. The following example displays alternating lines in different colors:

<asp:Repeater  RunAt="server"> <ItemTemplate> <%# Container.DataItem %><br> </ItemTemplate> <AlternatingItemTemplate> <span style="background-color:gainsboro;width:128;"> <%# Container.DataItem %> </span><br> </AlternatingItemTemplate> </asp:Repeater>

Header templates and footer templates enable a Repeater to render HTML elements that require start and end tags (such as tables and lists):

<asp:Repeater  RunAt="server"> <HeaderTemplate> <ul> </HeaderTemplate> <ItemTemplate> <li><%# Container.DataItem %><br> </ItemTemplate> <AlternatingItemTemplate> <li><span style="background-color:gainsboro;width:128;"> <%# Container.DataItem %> </span><br> </AlternatingItemTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater>

Repeater controls also support separator templates delimited by <SeparatorTemplate> and </SeparatorTemplate> tags. You can use them to place separators (for example, <hr> tags) between individual items.

In practice, Repeater controls rarely bind to simple arrays. Typically, they bind to more complex data sources such as DataReader and DataSet objects. The following example uses a Repeater to display the books listed in the Pubs database that comes with Microsoft SQL Server. Each item consists of a book title obtained from the title field of the current record and a price obtained from the price field, as shown in Figure 6-2.

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:Repeater  RunAt="server"> <ItemTemplate> <%# DataBinder.Eval (Container.DataItem, "title") +  " (" + DataBinder.Eval (Container.DataItem, "price", "{0:c}") +  ")" %><br> </ItemTemplate> </asp:Repeater> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=pubs;uid=sa;pwd="); try { connection.Open (); SqlCommand command = new SqlCommand ("select * from titles where price != 0", connection); SqlDataReader reader = command.ExecuteReader (); MyRepeater.DataSource = reader; MyRepeater.DataBind (); } finally { connection.Close (); } } } </script>

Figure 6-2

A portion of the Pubs database rendered by a Repeater control.

Note the use of DataBinder.Eval, which is common in data-binding expressions. DataBinder is a class defined in the System.Web.UI namespace; Eval is a static method that uses reflection to evaluate a data-binding expression. The second parameter passed to DataBinder.Eval identifies a field in the current record; the optional third parameter is a formatting string specifying how that field is converted into a string. DataBinder.Eval vastly simplifies data-binding syntax. Without it, the Repeater control in the previous example would have to have been defined this way:

<asp:Repeater  RunAt="server"> <ItemTemplate> <%# ((System.Data.Common.DbDataRecord) Container.DataItem)["title"] + " (" + String.Format ("{0:c}", ((System.Data.Common.DbDataRecord) Container.DataItem)["price"]) + ")" %><br> </ItemTemplate> </asp:Repeater>

Besides simplifying data-binding syntax, DataBinder.Eval provides an added level of indirection between data-binding expressions and data sources. Without DataBinder.Eval, changing the data source from a DataReader to a DataSet would also require changing the data-binding expression. With DataBinder.Eval, no such change is necessary.

The Repeater class defines three events: ItemCreated, ItemDataBound, and ItemCommand. ItemCreated and ItemDataBound events fire each time an item is created and each time an item binds to a data source, respectively. They let the developer further customize a Repeater s output. ItemCommand events fire when a button declared within a Repeater control is clicked. The CommandSource property of the RepeaterCommandEventArgs passed to the event handler identifies the button that prompted the event. The CommandName and CommandArgument properties identify the values of the same names assigned to the button control. The following example wraps the output from the Repeater control in a table and adds an Add to Cart button to each row (Figure 6-3). Clicking a button displays the selected title at the bottom of the page:

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:Repeater  OnItemCommand="OnItemCommand" RunAt="server"> <HeaderTemplate> <table border="1"> <tr> <td align="center">Title</td> <td align="center">Price</td> <td align="center">Action</td> </tr> </HeaderTemplate> <ItemTemplate> <tr> <td> <%# DataBinder.Eval (Container.DataItem, "title") %> </td> <td align="center"> <%# DataBinder.Eval (Container.DataItem, "price",  "{0:c}") %> </td> <td align="center"> <asp:Button Text="Add to Cart" RunAt="server" CommandArgument='<%# DataBinder.Eval (Container.DataItem, "title") %>' /> </td> </tr> </ItemTemplate> <FooterTemplate> </table> </FooterTemplate> </asp:Repeater> <asp:Label  RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=pubs;uid=sa;pwd="); try { connection.Open (); SqlCommand command; command = new SqlCommand ("select * from titles where price != 0", connection); SqlDataReader reader = command.ExecuteReader (); MyRepeater.DataSource = reader; MyRepeater.DataBind (); } finally { connection.Close (); } } } void OnItemCommand (Object sender, RepeaterCommandEventArgs e) { Output.Text = e.CommandArgument.ToString (); } </script>

To simplify the process of figuring out which item in the control corresponds to the button that was clicked, this example initializes the CommandArgument property of each Button control with the book title stored in the current record s title field using a data-binding expression in the <asp:Button> tag.

Figure 6-3

Repeater control with buttons.

The MyComicsRepeater Page

For a first-hand look at a Repeater control in action, check out the Web form in Figure 6-4. Named MyComicsRepeater.aspx, it uses a Repeater control to display the contents of a SQL Server database named MyComics that stores information about a collection of fictitious comic books. Each item in the control includes a thumbnail-size cover scan, a title and issue number, and other relevant information about the corresponding comic. (In case you wondered, the CGC column specifies whether the numerical grade in the column to the left is an official CGC grade or an estimated CGC grade. CGC stands for Comics Guaranty Corporation, one of the world s most respected authorities on comic books.) Clicking a thumbnail image displays a full-size scan of the cover.

Before you can run MyComicsRepeater.aspx, you must do the following:

  1. Create the MyComics database. The CD that comes with this book contains a SQL script file named MyComics.sql that creates and initializes the database. To execute the script, open a command prompt window and type

    osql -U sa -P -i mycomics.sql

    in the directory where MyComics.sql is stored. This assumes, of course, that Microsoft SQL Server is installed on your PC.

  2. Either copy the MyComics folder that was created when you installed the CD to \Inetpub\wwwroot, or use the IIS configuration manager to make MyComics a virtual directory.

If you copy MyComics to wwwroot, you can open the Web form by typing

http://localhost/mycomicsrepeater.aspx

into your browser s address bar. If you make MyComics a virtual directory and name it MyComics, type the following instead:

http://localhost/mycomics/mycomicsrepeater.aspx

Figure 6-4

The MyComicsRepeater page.

MyComicsRepeater.aspx is listed in Figure 6-5. At its heart is a Repeater control that binds to the records returned by a query executed in Page_Load. Each item rendered by the Repeater includes a table for visual formatting purposes. Most of the data-binding expressions do little more than embed text in table cells. These, however, do more:

<a href='Images/Large/<%# DataBinder.Eval (Container.DataItem, "LargeCover") %>'> <img src='/books/4/347/1/html/2/Images/Small/<%# DataBinder.Eval (Container.DataItem, "SmallCover") %>' </a>

This code embeds an <img> tag in each item output by the Repeater control. The Src attribute references one of the thumbnails in the Images\Small subdirectory. Furthermore, the <img> tag is enclosed in an <a> tag whose Href attribute references one of the larger and more detailed cover images in the Images\Large subdirectory. That s why clicking a thumbnail displays a close-up image of the comic book cover. The names of the files containing the cover scans come from the database s SmallCover and LargeCover fields and are appended to the path names to form URLs.

MyComicsRepeater.aspx

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <h1>My Comics (Repeater)</h1> <hr> <form runat="server"> <table width="100%"> <tr> <td width="104" /> <td> <table cellpadding="4" width="100%"> <tr height="48" bgcolor="yellow"> <td width="40%" align="center"> Title </td> <td width="15%" align="center"> Number </td> <td width="15%" align="center"> Year </td> <td width="15%" align="center"> Rating </td> <td width="15%" align="center"> CGC Rated? </td> </tr> </table> </td> </tr> </table> <asp:Repeater  RunAt="server"> <ItemTemplate>

 <table width="100%"> <tr> <td width="104"> <a href='Images/Large/<%# DataBinder.Eval (Container.DataItem, "LargeCover") %>'> <img src='/books/4/347/1/html/2/Images/Small/<%# DataBinder.Eval (Container.DataItem, "SmallCover") %>' </a> </td> <td> <table cellpadding="4" height="100%" width="100%"> <tr height="48" bgcolor="gainsboro"> <td width="40%"> <%# DataBinder.Eval (Container.DataItem, "Title") %> </td> <td width="15%" align="center"> <%# DataBinder.Eval (Container.DataItem, "Number") %> </td> <td width="15%" align="center"> <%# DataBinder.Eval (Container.DataItem, "Year") %> </td> <td width="15%" align="center"> <%# DataBinder.Eval (Container.DataItem, "Rating", "{0:f1}") %> </td> <td width="15%" align="center"> <%# ((bool) DataBinder.Eval (Container.DataItem, "CGC")) ?  "Yes" : "No" %> </td> </tr> <tr> <td colspan="5"> <%# DataBinder.Eval (Container.DataItem, "Comment") %> </td> </tr> </table> </td> </tr> </table> </ItemTemplate> </asp:Repeater> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=mycomics;uid=sa;pwd="); try { connection.Open (); SqlCommand command; command = new SqlCommand ("select * from books order by title, number", connection); SqlDataReader reader = command.ExecuteReader (); MyRepeater.DataSource = reader; MyRepeater.DataBind (); } finally { connection.Close (); } } } </script>

Figure 6-5

MyComicsRepeater source code.

DataList Controls

DataList controls are similar to Repeater controls, but they include features that Repeaters don t. Specifically, they add support for multicolumn formatting, item selection, and item editing. Multicolumn layouts are controlled with the RepeatColumns and RepeatDirection properties. Item selection is controlled with the SelectedIndex property, which holds the 0-based index of the item that s currently selected, and the SelectedItemStyle and SelectedItemTemplate properties, which govern the appearance of items that are in the selected state. To enable users to edit the items in a DataList, use the control s EditItemStyle and EditItemTemplate properties to define the appearance of the item that s being edited. The related EditItemIndex property specifies which item is currently being edited.

Developers accustomed to working with Windows controls are often surprised to learn that the selection in a DataList is controlled manually. A DataList doesn t automatically highlight an item when the item is clicked. Rather, it s up to a server-side event handler to do the highlighting by setting the control s SelectedIndex property equal to the index of the item. Each item rendered by a DataList that supports selection must include a control that activates a server-side event handler. In the following example, the DataList s item template includes a LinkButton control. When the LinkButton is clicked, the DataList fires an ItemCommand event, and the OnItemCommand handler selects the item that was clicked. For good measure, this DataList also takes advantage of multicolumn formatting by arranging its items in two columns:

<html> <body> <form runat="server"> <asp:DataList  RunAt="server" RepeatColumns="2" RepeatDirection="Horizontal" OnItemCommand="OnItemCommand"> <ItemTemplate> <asp:LinkButton Text="<%# Container.DataItem %>" RunAt="server" /><br> </ItemTemplate> <SelectedItemStyle BackColor="gainsboro" /> </asp:DataList> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { string[] beatles = { "John", "Paul", "George", "Ringo" }; MyDataList.DataSource = beatles; MyDataList.DataBind (); } } void OnItemCommand (Object sender, DataListCommandEventArgs e) { MyDataList.SelectedIndex = e.Item.ItemIndex; } </script>

The <SelectedItemStyle> tag in this example highlights the currently selected item by setting its background color to gainsboro (light gray). Other aspects of the item s appearance, however, are defined by the item template. If you want to, you can use a <SelectedItemTemplate> tag to specify the look of selected items independent of the item template.

Incidentally, the statement

<SelectedItemStyle BackColor="gainsboro" />

initializes the subproperty named BackColor of the property named SelectedItemStyle. If you d prefer, you can initialize subproperties in the tag that declares a control by separating property and subproperty names with hyphens, as in

<asp:DataList  RunAt="server" RepeatColumns="2" RepeatDirection="Horizontal" OnItemCommand="OnItemCommand" SelectedItemStyle-BackColor="gainsboro">

This syntax isn t limited to DataList controls. It s applicable to all controls that implement properties and subproperties, including Repeater, DataGrid, and Calendar controls.

The MyComicsDataList Page

The Web form in Figure 6-6 uses a DataList rather than a Repeater to put a face on the MyComics database. The underlying ASPX file, MyComicsDataList.aspx, appears in Figure 6-7. The DataList s item template renders a one-row, two-column table whose left cell contains a thumbnail cover image and whose right cell contains the comic book title, issue number, and other information. The top row of text is rendered as a LinkButton. Clicking the LinkButton invokes OnItemCommand, which selects the item that was clicked and displays descriptive text in the yellow bar at the top of the page. That text comes from the database s Comment field, which is passed to the event handler using the LinkButton s CommandArgument property. As with the Repeater control version of this page, clicking one of the cover thumbnails displays a close-up scan of the cover, thanks to an <a> tag surrounding the <img> tag emitted by the DataList control.

Figure 6-6

The MyComicsDataList page.

MyComicsDataList.aspx

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <h1>My Comics (DataList)</h1> <hr> <form runat="server"> <asp:Table Width="100%" Height="32" CellPadding="4" BackColor="yellow" RunAt="server"> <asp:TableRow> <asp:TableCell  /> </asp:TableRow> </asp:Table> <br> <asp:DataList  RepeatColumns="3" RepeatDirection="Horizontal" CellPadding="4" OnItemCommand="OnItemCommand" RunAt="server"> <ItemTemplate> <table width="100%" cellpadding="4">

 <tr> <td width="100"> <a href='Images/Large/<%# DataBinder.Eval (Container.DataItem, "LargeCover") %>'> <img src='/books/4/347/1/html/2/Images/Small/<%# DataBinder.Eval (Container.DataItem, "SmallCover") %>'> </a> </td> <td valign="top"> <asp:LinkButton CommandName="Select" RunAt="server" CommandArgument='<%# DataBinder.Eval (Container.DataItem, "Comment") %>' Text='<%# DataBinder.Eval (Container.DataItem,  "Title") + " " + DataBinder.Eval (Container.DataItem,  "Number") %>' /><br> <%# DataBinder.Eval (Container.DataItem,  "Year") %><br> <%# DataBinder.Eval (Container.DataItem, "Rating",  "{0:f1}") %><br> </td> </tr> </table> </ItemTemplate> <SelectedItemStyle BackColor="gainsboro" /> </asp:DataList> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection  ("server=localhost;database=mycomics;uid=sa;pwd="); try { connection.Open (); SqlCommand command; command = new SqlCommand ("select * from Books order by title, number", connection); SqlDataReader reader = command.ExecuteReader (); MyDataList.DataSource = reader; MyDataList.DataBind (); } finally { connection.Close (); } } } void OnItemCommand (Object sender, DataListCommandEventArgs e) { if (e.CommandName == "Select") { MyDataList.SelectedIndex = e.Item.ItemIndex; Output.Text = e.CommandArgument.ToString (); } } </script>

Figure 6-7

MyComicsDataList source code.

DataGrid Controls

DataGrid controls are the most complex of the data-bound Web controls for the simple reason that they offer the richest variety of options. The DataGrid s purpose is to display tabular data. A single DataGrid control can replace reams of old ASP code that queries a database and manually outputs a table using repeated calls to Response.Write. Here s an example that uses a DataGrid to display the contents of the SQL Server Pubs database s Titles table. The output is shown in Figure 6-8.

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid  RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=pubs;uid=sa;pwd="); try { connection.Open (); SqlCommand command = new SqlCommand ("select * from titles where price != 0", connection); SqlDataReader reader = command.ExecuteReader (); MyDataGrid.DataSource = reader; MyDataGrid.DataBind (); } finally { connection.Close (); } } } </script>

Figure 6-8

Bare-bones DataGrid control.

That s a lot of output for a relatively small amount of code, but aesthetically, the output leaves something to be desired. Fortunately, DataGrid controls support a wide range of formatting options. Some of those options are controlled by using properties such as BorderColor and BackColor as attributes in control tags. Others are exercised by using the Columns property to specify what columns should appear in the table and how those columns should be rendered. A DataGrid supports the following column types:

Column Type

Description

BoundColumn

Creates a column whose content comes from a field in the data source.

ButtonColumn

Creates a column of buttons (push buttons or link buttons).

EditColumn

Creates a column of buttons enabling DataGrid items to be edited and changes to be accepted or rejected.

HyperLinkColumn

Creates a column of hyperlinks. Hyperlink text can be static or drawn from a field in the data source.

TemplateColumn

Creates a column of items whose appearance is defined by a UI template.

By default, a DataGrid control contains one BoundColumn for every field in the data source. You can change that by setting the DataGrid s AutoGenerateColumns property to false and manually creating columns. The following example uses a DataGrid to display the contents of the Pubs database s Titles table the same table depicted in Figure 6-8. But this time, the results are more pleasing to the eye, as you can see in Figure 6-9.

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid AutoGenerateColumns="false" CellPadding="2" BorderWidth="1" BorderColor="lightgray" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" Width="90%" OnItemCommand="OnItemCommand" RunAt="server"> <Columns> <asp:BoundColumn HeaderText="Item ID" DataField="title_id" /> <asp:BoundColumn HeaderText="Title" DataField="title" /> <asp:BoundColumn HeaderText="Price" DataField="price" DataFormatString="{0:c}" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="right" /> <asp:ButtonColumn HeaderText="Action" Text="Add to Cart" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" CommandName="AddToCart" /> </Columns> <HeaderStyle BackColor="teal" ForeColor="white" Font-Bold="true" /> <ItemStyle BackColor="white" ForeColor="darkblue" /> <AlternatingItemStyle BackColor="beige" ForeColor="darkblue" /> </asp:DataGrid> <br> <asp:Label  RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=pubs;uid=sa;pwd="); try { connection.Open (); SqlCommand command = new SqlCommand ("select * from titles where price != 0", connection); SqlDataReader reader = command.ExecuteReader (); MyDataGrid.DataSource = reader; MyDataGrid.DataBind (); } finally { connection.Close (); } } } void OnItemCommand (Object sender, DataGridCommandEventArgs e) { if (e.CommandName == "AddToCart") Output.Text = e.Item.Cells[1].Text; } </script>

This DataGrid contains three BoundColumns and one ButtonColumn. The BoundColumns bind to the title_id, title, and price fields of the data source, as indicated by the DataField attributes. The ButtonColumn renders a column of LinkButtons labeled Add to Cart. Clicking a LinkButton activates OnItemCommand, which uses the Label control at the bottom of the page to display the title of the book whose Add to Cart button was clicked. Just like Repeater controls and DataList controls, a DataGrid control that renders buttons fires an ItemCommand event whenever one of those buttons is clicked.

When an ItemCommand event fires, the handler receives a DataGridCommandEventArgs whose Item property represents the row whose button was clicked. As the example in the previous paragraph demonstrates, the handler can read the contents of the row by reading the TableCells in Item s Cells collection. Each TableCell s Text property exposes the text contained in one of the row s cells.

Figure 6-9

Customized DataGrid control.

Sortable DataGrid Controls

By default, items appear in a DataGrid in the same order in which they re enumerated from the data source. You can sort the contents of a DataGrid by binding to a DataView instead of a DataReader or DataSet. DataView is an ADO.NET class used to create logical views of the records in a data source. A DataView can be sorted by assigning a sort expression to its Sort property. For example, the sort expression Title ASC sorts the DataView in ascending order based on the contents of its Title column. The sort expression Price DESC sorts the DataView in descending order based on the contents of its Price column. The following code fragment demonstrates how you d modify the Page_Load method in the previous example to sort the DataGrid output by book title:

void Page_Load (Object sender, EventArgs e) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from titles where price != 0",  "server=localhost;database=pubs;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); DataView view = new DataView (ds.Tables[0]); view.Sort = "Title ASC"; MyDataGrid.DataSource = view; MyDataGrid.DataBind (); }

In this example, the DataView wraps a DataTable. The DataTable comes from a DataSet initialized with a DataAdapter. Setting the DataView s Sort property to Title ASC sorts items enumerated from the DataView by title.

DataGrids also support interactive sorting. Here s a recipe for creating a sortable DataGrid one whose contents can be sorted by clicking a column header:

  1. Set the DataGrid s AllowSorting property to true.

  2. Include a SortExpression attribute in each BoundColumn that supports sorting. The sort expression Title ASC performs an ascending sort on the Title field.

  3. Write a handler for the SortCommand event that a DataGrid fires when the header atop a sortable column is clicked. In the event handler, bind the DataGrid to a DataView. Initialize the DataView s Sort property with the sort expression in the SortExpression property of the DataGridSortCommandEventArgs passed to the event handler.

  4. Use an OnSortCommand attribute in the <asp:DataGrid> tag to connect the DataGrid to the SortCommand event handler.

To demonstrate, the following example shows how to modify the DataGrid control shown in the previous section to support interactive sorting by title and price. Changes are highlighted in bold:

<asp:DataGrid AutoGenerateColumns="false" CellPadding="2" BorderWidth="1" BorderColor="lightgray" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" Width="90%" OnItemCommand="OnItemCommand" RunAt="server" AllowSorting="true" OnSortCommand="OnSort"> <Columns> <asp:BoundColumn HeaderText="Item ID" DataField="title_id" /> <asp:BoundColumn HeaderText="Title" DataField="title" SortExpression="title ASC" /> <asp:BoundColumn HeaderText="Price" DataField="price" DataFormatString="{0:c}" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="right" SortExpression="price DESC" /> . . . </asp:DataGrid> . . . // In the <script> block void OnSort (Object sender, DataGridSortCommandEventArgs e) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from titles where price != 0",  "server=localhost;database=pubs;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); DataView view = new DataView (ds.Tables[0]); view.Sort = e.SortExpression.ToString (); // e.g., "Title ASC" MyDataGrid.DataSource = view; MyDataGrid.DataBind (); }

The value passed in the SortExpression property of the DataGridSortCommandEventArgs is the same SortExpression value assigned to the BoundColumn whose header was clicked. Figure 6-10 shows how the DataGrid appears after it s sorted by title. Note the underlining that appears in the headers atop the Title and Price columns indicating that these columns support sorting.

Figure 6-10

Sortable DataGrid control.

Pageable DataGrid Controls

DataGrid controls also support paging. Paging enables you to paginate the output when you bind to a data source containing a long list of items. The following example displays records from the Microsoft SQL Server Northwind database s Products table in a pageable DataGrid:

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid AutoGenerateColumns="false" CellPadding="2" BorderWidth="1" BorderColor="lightgray" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" Width="90%" RunAt="server" AllowPaging="true" PageSize="10" OnPageIndexChanged="OnNewPage"> <Columns> <asp:BoundColumn HeaderText="Product ID" DataField="ProductID" HeaderStyle-HorizontalAlign="center"  ItemStyle-HorizontalAlign="center" /> <asp:BoundColumn HeaderText="Product Name" DataField="ProductName" HeaderStyle-HorizontalAlign="center" /> <asp:BoundColumn HeaderText="Unit Price" DataField="UnitPrice" HeaderStyle-HorizontalAlign="center"  ItemStyle-HorizontalAlign="right" DataFormatString="{0:c}" /> <asp:BoundColumn HeaderText="Unit" DataField="QuantityPerUnit" HeaderStyle-HorizontalAlign="center" /> </Columns> <HeaderStyle BackColor="teal" ForeColor="white" Font-Bold="true" /> <ItemStyle BackColor="white" ForeColor="darkblue" /> <AlternatingItemStyle BackColor="beige" ForeColor="darkblue" /> </asp:DataGrid> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from products",  "server=localhost;database=northwind;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); MyDataGrid.DataSource = ds; MyDataGrid.DataBind (); } } void OnNewPage (Object sender, DataGridPageChangedEventArgs e) { MyDataGrid.CurrentPageIndex = e.NewPageIndex; SqlDataAdapter adapter = new SqlDataAdapter ("select * from products",  "server=localhost;database=northwind;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); MyDataGrid.DataSource = ds; MyDataGrid.DataBind (); } </script>

The output from this code is shown in Figure 6-11. Clicking one of the arrows at the bottom of the page displays the next or previous page. The AllowPaging attribute in the <asp:DataGrid> tag enables paging. The PageSize attribute sizes each page to display 10 items, and the OnPageIndexChanged attribute identifies the event handler OnNewPage that s called when an arrow is clicked. OnNewPage displays the next or previous page by reinitializing the DataSet and setting the DataGrid s CurrentPageIndex property equal to the page number passed in the DataGridPageChangedEventArgs. The DataGrid does the hard part by extracting the data for the current page from the DataSet.

Figure 6-11

Pageable DataGrid control.

You can further customize a pageable DataGrid by using its PagerStyle property. Adding the following attributes to the <asp:DataGrid> tag changes the arrows displayed at the bottom of the control to the strings Previous Page and Next Page:

PagerStyle-PrevPageText="Previous Page" PagerStyle-NextPageText="Next Page"

The attribute shown in the next statement replaces the arrows with page numbers, providing random access to the DataGrid s pages:

PagerStyle-Mode="NumericPages"

For a list of other changes you can effect with PagerStyle, consult the list of DataGridPagerStyle members in the .NET Framework SDK documentation.

One drawback to paging a DataGrid using the technique shown in the previous example is that it s inefficient. OnNewPage retrieves the entire Products table each time it s called, even though it needs only 10 records. That s why DataGrid controls support an alternative form of paging called custom paging. Setting a DataGrid s AllowCustomPaging property to true enables custom paging and frees the PageIndexChanged handler to fetch just the records shown on the current page. Custom paging can deliver dramatic performance benefits when the data source contains hundreds or thousands of records rather than just a few.

The MyComicsDataGrid Page

The Web page shown in Figure 6-12 rounds out this chapter s treatment of data-bound Web controls by using a DataGrid to expose the contents of the MyComics database used in earlier examples. In addition to showing how to use BoundColumns to expose selected fields in a data source, MyComicsDataGrid.aspx, shown in Figure 6-13, demonstrates how to use TemplateColumns to create user-defined columns. The statements

<asp:TemplateColumn HeaderText="CGC" HeaderStyle-HorizontalAlign="center"> <ItemTemplate> <center> <%# ((bool) DataBinder.Eval (Container.DataItem,  "CGC")) == false ? "N" : "Y" %> </center> </ItemTemplate> </asp:TemplateColumn>

create a column that programmatically binds to the database s CGC field and displays either Y if the field contains a 1 or N if it contains a 0. DataGrid controls don t support UI templates per se, but TemplateColumns do. One common use for TemplateColumns is to create columns containing TextBox controls that the user can type information into.

The DataGrid in this example also uses a ButtonColumn to put View Cover buttons in the grid s leftmost column. Clicking a button fires an ItemCommand event that activates the page s OnItemCommand method, which displays a close-up of the comic book cover by calling Response.Redirect with the URL of the corresponding image in the Images/Large directory. Response.Redirect is one of the most important methods in all of ASP.NET; it transfers control to another Web page. You ll learn more about it later in this chapter.

Figure 6-12

The MyComicsDataGrid page.

MyComicsDataGrid.aspx

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <h1>My Comics (DataGrid)</h1> <hr> <form runat="server"> <center> <asp:DataGrid  AutoGenerateColumns="false" BorderWidth="1" BorderColor="lightgray" CellPadding="2" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" OnItemCommand="OnItemCommand" OnSortCommand="OnSort" Width="90%" AllowSorting="true" RunAt="server"> <Columns> <asp:ButtonColumn ButtonType="PushButton" HeaderText="Cover" Text="View" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" CommandName="ViewComic" /> <asp:BoundColumn HeaderText="Title" DataField="Title" SortExpression="Title ASC"

 HeaderStyle-HorizontalAlign="center" /> <asp:BoundColumn HeaderText="Number" DataField="Number" SortExpression="Number ASC" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" /> <asp:BoundColumn HeaderText="Year" DataField="Year" SortExpression="Year ASC" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" /> <asp:BoundColumn HeaderText="Rating" DataField="Rating" DataFormatString="{0:f1}" SortExpression="Rating ASC" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" /> <asp:TemplateColumn HeaderText="CGC" HeaderStyle-HorizontalAlign="center"> <ItemTemplate> <center> <%# ((bool) DataBinder.Eval (Container.DataItem,  "CGC")) == false ? "N" : "Y" %> </center> </ItemTemplate> </asp:TemplateColumn> <asp:BoundColumn HeaderText="Large Cover" DataField="LargeCover" HeaderStyle-HorizontalAlign="center" /> <asp:BoundColumn HeaderText="Small Cover" DataField="SmallCover" HeaderStyle-HorizontalAlign="center" /> </Columns> <HeaderStyle BackColor="teal" ForeColor="white" Font-Bold="true" /> <ItemStyle BackColor="white" ForeColor="darkblue" /> <AlternatingItemStyle BackColor="beige" ForeColor="darkblue" /> </asp:DataGrid> </center> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from books order by title, number",  "server=localhost;database=mycomics;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); MyDataGrid.DataSource = ds; MyDataGrid.DataBind (); } } void OnSort (Object sender, DataGridSortCommandEventArgs e) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from books order by title, number",  "server=localhost;database=mycomics;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); DataView view = new DataView (ds.Tables[0]); view.Sort = e.SortExpression.ToString (); MyDataGrid.DataSource = view; MyDataGrid.DataBind (); } void OnItemCommand (Object sender, DataGridCommandEventArgs e) { if (e.CommandName == "ViewComic") Response.Redirect ("Images/Large" + e.Item.Cells[6].Text); } </script>

Figure 6-13

MyComicsDataGrid source code.



Programming Microsoft  .NET
Applied MicrosoftNET Framework Programming in Microsoft Visual BasicNET
ISBN: B000MUD834
EAN: N/A
Year: 2002
Pages: 101

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