Using a DataList Control to Repeat Composite Controls
Early access to new technology is a nice benefit of writing computer books. Using .NET for several years now and having written well over 100,000 lines of production code, I have come to appreciate the simplicity and utility of controls like DataList . The DataList control is easy and powerful to use. However, if you are using it (or the DataGrid control) just to display data sets or simple arrays of data, you are missing out on a subtle power that is also readily available. I mentioned it earlier in the chapter. If you create composite controls from a user control, you can also use these composite controls as elements of a DataList , Repeater , or DataGrid control. That is, you can repeat complex user interfaces automatically by placing them inside of the DataList control. (For our purposes, understand that when I say DataList , I also mean DataGrid and Repeater too. You can use these techniques for DataList , DataGrid , and Repeater controls.)
Remember earlier that we elected to display one contact record at a time by using labels and text boxes? Well, we can also define the presentation by using labels and text boxes on a user control, placing the user control in a DataList control, and letting the DataList control repeat and manage paging for us.
Defining the Composite User Control
Our solution is comprised of three basic parts . We need a source of data. For this we'll use the Contacts data view we have been using all along. (All the code for this section is defined in CompositeControlDemo.sln .) We will need a user control defined to display one row of data in the Contacts data view, and we will need a Web page with a DataList control on it. The DataList control will be the recipient of the composite user control. Since we are reusing the Contacts data view from earlier in the chapter, we will start with the user control.
Implementing the User Control
We will use an HTML table with four rows and four columns to manage the layout of the Contacts composite control. Figure 16.11 shows that there is one control in each cell . The rhythm is a label followed by a text box for the data associated with that label. This is repeated a second time for each row. Because there are six fields in the Contacts data view, we will repeat this process for three rows. (Two columns by three rows yields the correct number of data elements in Contacts .) The last row has one cell that spans all four columns. I added a <HR> (horizontal rule) tag to that row to provide a visual cue to the user. The actual HTML to create this view is shown in Listing 16.12.
Listing 16.12 The HTML for the Contacts User Control
<%@ Control Language="vb" AutoEventWireup="false" Codebehind="Contacts.ascx.vb" Inherits="CompositeControlDemo.Contacts" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> <TABLE id="Table1" borderColor="black" cellSpacing="0" cellPadding="2" width="100%" border="2"> <TR> <TD align="right"><asp:label id="Label1" runat="server">Company Name:</asp:label></TD> <TD><asp:textbox id=TextBoxCompanyName runat="server" Text='<%# DataRow("CompanyName") %>' ></asp:textbox></TD> <TD align="right"><asp:label id="Label2" runat="server">Country:</asp:label></TD> <TD><asp:textbox id=TextBoxCountry runat="server" Text='<%# DataRow("Country") %>' ></asp:textbox></TD> </TR> <TR> <TD align="right"><asp:label id="Label3" runat="server">Title:</asp:label></TD> <TD><asp:textbox id=TextBoxTitle runat="server" Text='<%# DataRow("ContactTitle") %>' ></asp:textbox></TD> <TD align="right"><asp:label id="Label4" runat="server">Name:</asp:label></TD> <TD><asp:textbox id=TextBoxName runat="server" Text='<%# DataRow("ContactName") %>' ></asp:textbox></TD> </TR> <TR> <TD align="right"><asp:label id="Label5" runat="server">Phone:</asp:label></TD> <TD><asp:textbox id=TextBoxPhone runat="server" Text='<%# DataRow("Phone") %>' ></asp:textbox></TD> <TD align="right"> <asp:Label id="Label6" runat="server">Fax:</asp:Label> </TD> <TD> <asp:TextBox id="TextBoxFax" runat="server" Text='<%# DataRow("Fax") %>' > </asp:TextBox></TD> </TR> <TR> <TD align="right" colSpan="4"> <HR width="100%" SIZE="1"> </TD> </TR> </TABLE>
Figure 16.11. The design-time results of Listing 16.12, shown with an exaggerated border and cell spacing and cell padding for clarity.
Defining the Data Member
We won't be binding data to the user control. Instead we will be binding data to a DataList control, and each time a row is created we will need to send that row of data to the instance of the user control. This is a little tricky, but by breaking it up into chunks I think you will see it isn't too hard. For now all we need to do is acknowledge that the user control will be getting an instance of a DataRowView objectone row of data from the Contacts data view. We can add a public field to accept this value. I defined it in the code-behind file for the user control as follows :
Public DataRow As DataRowView
Defining the Binding Statements for the Controls
The final step for the user control is to get the data into the TextBox.Text property. We know that we can use a simple binding statement to accomplish this, and you already learned how to write binding statements earlier in the chapter. The data itself actually will come from the DataRow field we defined, so we can define the binding statements in the context of the DataRow field. The actual binding statements I used are set in bold in Listing 16.12. This is all we have to do to create the user control. Now we need to use the user control.
Defining the Web Page with the DataList Control
At this point the DataList control comes into play. We will add a DataList control to the default Web page from CompositeControlDemo.sln . The DataList control accepts the user control, and we'll bind the Contacts object to the DataList control. We'll use a binding statement to get each row of data from the DataList control into each instance of the user control. (Figure 16.12 shows the results at runtime.)
Figure 16.12. The DataList control automatically repeats the Contacts user control for us.
Follow these numbered steps to complete the example.
Listing 16.13 The HTML for the Web Page Demonstrating a Template Item
The user control is shown in bold font within the <ItemTemplate> tag. Understand the DataRow clause to be an assignment of the Contacts.DataRow user control.
There are several ways to get data into a user control used as a template. Using a binding statement is just one very direct way. Keep in mind that whether you accomplish this bit with a binding statement or in the code-behind file, all you are doing is assigning data to an object's property. It just isn't as clear in the HTML view.
I want to point out one more thing. If you recall, we put text boxes in the user control, and that control went into the ItemTemplate. If we wanted to make a clearer distinction between a browse view and an edit view, we could create two user controls, one with labels only and the other with text boxes. The labels-only version could be placed in the ItemTemplate, and the text-box version could be placed in the EditItemTemplate. This distinction would allow you to present a clearly editable version and a clearly read-only version.
To switch to edit mode you need to add a LinkButton object to the ItemTemplate, set the LinkButton.CommandName property to Edit , and implement a DataList.EditCommand event handler. In the EditCommand event handler, set the DataList.EditItemIndex argument to e.Item.ItemIndex (the event handler's argument index) and rebind the DataList control. This step will switch you to EditItemTemplateControls . To save your changes you will need a symmetric Update LinkButton object in the EditItemTemplate. When the user clicks the Update button, you will need to save your changes, set the DataList.EditItemIndex argument to -1 , and rebind the DataList control.
I made a copy of CompositeControlDemo.sln ( CompositeControlDemo2.sln ) that demonstrates how to switch between a read-only view in the ItemTemplate to an edit view in the EditItemTemplate. I also added Cancel and Delete buttons to invoke the Cancel and Delete methods , respectively. Actually saving the modified changes was stubbed out for the example (and you can return to Chapters 11 and 12 for the update code). Figure 16.14 shows the revised example from CompositeControlDemo2.sln . Notice that the first row is in edit mode and is rendered with TextBox controls, while the rest of the page is rendered with labels.
Figure 16.14. Two versions of a template control, one for editing (top row) and one for browsing (remaining rows).