The ASP.NET Framework is a framework. If you don’t like anything about the framework, you always have the option of extending it. In particular, if you discover that the standard databound controls in the framework don’t do everything you need, you can create a custom databound control.
In this chapter, you learn how to create custom controls that work like the ASP.NET GridView
, DetailsView
, ListView, and FormView
controls. In the first part of this chapter, you learn how to create controls that support templates. You learn how to implement controls that support both standard templates and two-way databinding templates. You also learn how to supply a control with a default template.
The last part of this chapter is devoted to the topic of databound controls. You learn about the new base control classes included in the framework that were supplied to make it easier to create custom databound controls. We create a custom templated databound control.
A template enables you to customize the layout of a control. Furthermore, a template can contain expressions that are not evaluated until runtime.
The ASP.NET Framework supports two types of templates. First, you can create a one-way databinding template. You use a one-way databinding template to display data items. In a one-way databinding template, you use the Eval()
expression to display the value of a data item.
Second, you have the option of creating a two-way databinding template. A two-way databinding template can be used not only to display data items, but also to update data items. You can use the Bind()
expression in a two-way databinding template to both display a data item and extract the value of a data item.
Typically, you use templates with a databound control. For example, the ListView
, GridView
, Repeater
, DataList
, FormView
, and DetailsView
controls all support an ItemTemplate
that enables you to format the data items that these controls display. However, you can use a template even when you are not displaying a set of data items. For example, the Login
control supports a LayoutTemplate
that enables you to customize the appearance of the Login form.
This part of this chapter concentrates on creating nondatabound controls that support templates. In the next part of this chapter, you learn how to use templates with databound controls.
You create a one-way databinding template by adding a property to a control that returns an object that implements the ITemplate
interface. The ITemplate
interface includes one method:
InstantiateIn
—. Instantiates the contents of a template in a particular control.
You are not required to implement the InstantiateIn()
method yourself. The ASP.NET Framework creates the method for you automatically. You call the InstantiateIn
method in your control to add the contents of a template to your control.
For example, the control in Listing 30.1 represents an article. The Article
control includes a template named ItemTemplate
. The ItemTemplate
is used to lay out the elements of the article: the title, author, and contents.
Example 30.1. Article.cs
using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace myControls { public class Article : CompositeControl { private string _title; private string _author; private string _contents; private ITemplate _itemTemplate; public string Title { get { return _title; } set { _title = value; } } public string Author { get { return _author; } set { _author = value; } } public string Contents { get { return _contents; } set { _contents = value; } } [TemplateContainer(typeof(Article))] [PersistenceMode(PersistenceMode.InnerProperty)] public ITemplate ItemTemplate { get { return _itemTemplate; } set { _itemTemplate = value; } } protected override void CreateChildControls() { _itemTemplate.InstantiateIn(this); } } }
Notice that the Article
control contains a property named ItemTemplate
that returns an object that implements the ITemplate
interface. Notice that this property is decorated with two attributes: a TemplateContainer
and a PersistenceMode
attribute.
The TemplateContainer
attribute is used to specify the type of control that will contain the template. In the case of the Article
control, the template will be contained in the Article
control itself. Therefore, the Article
control’s type is passed to the TemplateContainer
attribute.
The PersistenceMode
attribute indicates how a property is persisted in an ASP.NET page. The possible values are Attribute
, EncodedInnerDefaultProperty
, InnerDefaultProperty
, and InnerProperty
. We want to declare the ItemTemplate
like this:
<custom:Article runat="server"> <ItemTemplate> ... template contents ... </ItemTemplate> </custom:Article>
Because we want to declare the ItemTemplate
inside the Article
control, the PersistenceMode
attribute needs to be set to the value InnerProperty
.
The Article
control overrides the base WebControl
class’s CreateChildControls()
method. The ItemTemplate
is added as a child control to the Article
control. Any controls contained in the template become child controls of the current control.
The page in Listing 30.2 illustrates how you can use the Article
control and its ItemTemplate
.
Example 30.2. ShowArticle.aspx
<%@ Page Language="C#" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> void Page_Load() { Article1.Title = "Creating Templated Databound Controls"; Article1.Author = "Stephen Walther"; Article1.Contents = "Blah, blah, blah, blah..."; Article1.DataBind(); } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Article</title> </head> <body> <form id="form1" runat="server"> <div> <custom:Article id="Article1" Runat="server"> <ItemTemplate> <h1><%# Container.Title %></h1> <em>By <%# Container.Author %></em> <br /><br /> <%# Container.Contents %> </ItemTemplate> </custom:Article> </div> </form> </body> </html>
When you open the page in Listing 30.2, the contents of the ItemTemplate
are displayed (see Figure 30.1).
In the Page_Load()
method, the Title
, Author
, and Contents
properties of the article are set. Notice that these properties are used within databinding expressions within the Article
control’s ItemTemplate
. For example, the value of the Title
property is displayed with the following databinding expression:
<%# Container.Title %>
The Container
keyword refers to the current binding container. In this case, the binding container is the Article
control itself. Therefore, you can refer to any property of the Article
control by using the Container
keyword.
Notice that the Article
control’s DataBind()
method is called at the end of the Page_Load()
method. Don’t forget to call this method when you include databinding expressions in a template. If you don’t call this method, then the databinding expressions are never evaluated and displayed.
The previous section discussed the ITemplate
interface’s InstantiateIn()
method. Normally, you don’t implement the InstantiateIn()
method; you let the ASP.NET Framework do it for you. However, if you want to supply a control with a default template, then you need to implement this method.
The modified Article
control in Listing 30.3 includes a default template for the ItemTemplate
. The default template is used when an ItemTemplate
is not supplied.
Example 30.3. ArticleWithDefault.cs
using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace myControls { public class ArticleWithDefault : CompositeControl { private string _title; private string _author; private string _contents; private ITemplate _itemTemplate; public string Title { get { return _title; } set { _title = value; } } public string Author { get { return _author; } set { _author = value; } } public string Contents { get { return _contents; } set { _contents = value; } } [TemplateContainer(typeof(ArticleWithDefault))] [PersistenceMode(PersistenceMode.InnerProperty)] public ITemplate ItemTemplate { get { return _itemTemplate; } set { _itemTemplate = value; } } protected override void CreateChildControls() { if (_itemTemplate == null) _itemTemplate = new ArticleDefaultTemplate(); _itemTemplate.InstantiateIn(this); } } public class ArticleDefaultTemplate : ITemplate { public void InstantiateIn(Control container) { Label lblTitle = new Label(); lblTitle.DataBinding += new EventHandler(lblTitle_DataBinding); Label lblAuthor = new Label(); lblAuthor.DataBinding += new EventHandler(lblAuthor_DataBinding); Label lblContents = new Label(); lblContents.DataBinding += new EventHandler(lblContents_DataBinding); container.Controls.Add(lblTitle); container.Controls.Add(new LiteralControl("<br />")); container.Controls.Add(lblAuthor); container.Controls.Add(new LiteralControl("<br />")); container.Controls.Add(lblContents); } void lblTitle_DataBinding(object sender, EventArgs e) { Label lblTitle = (Label)sender; ArticleWithDefault container = (ArticleWithDefault)lblTitle. NamingContainer; lblTitle.Text = container.Title; } void lblAuthor_DataBinding(object sender, EventArgs e) { Label lblAuthor = (Label)sender; ArticleWithDefault container = (ArticleWithDefault)lblAuthor. NamingContainer; lblAuthor.Text = container.Author; } void lblContents_DataBinding(object sender, EventArgs e) { Label lblContents = (Label)sender; ArticleWithDefault container = (ArticleWithDefault)lblContents. NamingContainer; lblContents.Text = container.Contents; } } }
The control in Listing 30.3 is very similar to the control created in the previous section. However, notice that the CreateChildControls()
method has been modified. The new version of the CreateChildControls()
method tests whether there is an ItemTemplate
. If there is no ItemTemplate
, an instance of the ArticleDefaultTemplate
class is created.
The ArticleDefaultTemplate
class, which is also included in Listing 30.3, implements the ITemplate
interface. In particular, the class implements the InstantiateIn()
method. The instantiateIn()
method creates all the controls that will appear in the template.
In Listing 30.3, three Label
controls are created that correspond to the Title
, Author
, and Contents
properties. Notice that the DataBinding
event is handled for all three of these Label
controls. When the DataBind()
method is called, the DataBinding
event is raised for each child control in the Article
control. At that time, the values of the Title
, Author
, and Contents
properties are assigned to the Text
properties of the Label
controls.
The page in Listing 30.4 illustrates how you can use the modified Article
control.
Example 30.4. ShowArticleWithDefault.aspx
<%@ Page Language="C#" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> void Page_Load() { ArticleWithDefault1.Title = "Creating Templated Databound Controls"; ArticleWithDefault1.Author = "Stephen Walther"; ArticleWithDefault1.Contents = "Blah, blah, blah, blah..."; ArticleWithDefault1.DataBind(); } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Article with Default Template</title> </head> <body> <form id="form1" runat="server"> <div> <custom:ArticleWithDefault id="ArticleWithDefault1" Runat="server" /> </div> </form> </body> </html>
The ArticleWithDefault
control in Listing 30.4 does not include an ItemTemplate
. When the page is displayed in a browser, the contents of the ItemTemplate
are supplied by the ArticleDefaultTemplate
class (see Figure 30.2).
The databinding expressions used in the previous two sections might seem a little odd. For example, we used the following databinding expression to refer to the Title
property:
<%# Container.Title %>
When you use a databinding expression with one of the standard ASP.NET controls, such as the GridView
control, you typically use a databinding expression that looks like this:
<%# Eval("Title") %>
Why the difference? The standard ASP.NET controls support a simplified databinding syntax. If you want to support this simplified syntax in your custom controls, then you must implement the IDataItemContainer
interface.
The IDataItemContainer
includes the following three properties, which you are required to implement:
DataItem
—. Returns the value of the data item.
DataItemIndex
—. Returns the index of the data item from its data source.
DisplayIndex
—. Returns the index of the data item as it is displayed in a control.
Typically, you implement the IDataItemContainer
when creating a databound control. For example, you wrap up each record retrieved from a database table in an object that implements the IDataItemContainer
interface. That way, you can use a simplified databinding expression to refer to the value of a particular database record column.
In this section, we create a nondatabound control that supports the simplified databinding syntax. The control is named the Product
control, and it is included in Listing 30.5.
Example 30.5. Product.cs
using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace myControls { public class Product : CompositeControl { private ITemplate _itemTemplate; private ProductItem _item; public string Name { get { EnsureChildControls(); return _item.Name; } set { EnsureChildControls(); _item.Name = value; } } public Decimal Price { get { EnsureChildControls(); return _item.Price; } set { EnsureChildControls(); _item.Price = value; } } [TemplateContainer(typeof(ProductItem))] [PersistenceMode(PersistenceMode.InnerProperty)] public ITemplate ItemTemplate { get { return _itemTemplate; } set { _itemTemplate = value; } } protected override void CreateChildControls() { _item = new ProductItem(); _itemTemplate.InstantiateIn(_item); Controls.Add(_item); } } public class ProductItem : WebControl, IDataItemContainer { private string _name; private decimal _price; public string Name { get { return _name; } set { _name = value; } } public decimal Price { get { return _price; } set { _price = value; } } public object DataItem { get { return this; } } public int DataItemIndex { get { return 0; } } public int DisplayIndex { get { return 0; } } } }
The file in Listing 30.5 actually contains two classes: the Product
and the ProductItem
class. The Product
control includes an ItemTemplate
property. Notice that the TemplateContainer
attribute that decorates this property associates the ProductItem
class with the ItemTemplate
.
In the CreateChildControls()
method, the ItemTemplate
is instantiated into the ProductItem
class. The ProductItem
class, in turn, is added to the controls collection of the Product
class.
The ProductItem
class implements the IDataItemContainer
interface. Implementing the DataItemIndex
and DisplayIndex
properties is a little silly because there is only one data item. However, you are required to implement all the properties of an interface.
The page in Listing 30.6 illustrates how you can use the Product
control with the simplified databinding syntax.
Example 30.6. ShowProduct.aspx
<%@ Page Language="C#" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> void Page_Load() { Product1.Name = "Laptop Computer"; Product1.Price = 1254.12m; Product1.DataBind(); } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Product</title> </head> <body> <form id="form1" runat="server"> <div> <custom:Product id="Product1" Runat="Server"> <ItemTemplate> Name: <%# Eval("Name") %> <br /> Price: <%# Eval("Price", "{0:c}") %> </ItemTemplate> </custom:Product> </div> </form> </body> </html>
Notice that the Eval()
method is used in the Product
control’s ItemTemplate
. For example, the expression Eval("Name")
is used to display the product name. If you prefer, you can still use the Container.Name
syntax. However, the Eval()
syntax is more familiar to ASP.NET developers.
Two-way databinding is a feature that was introduced with the ASP.NET 2.0 Framework. Two-way databinding enables you to extract values from a template. You can use a two-way databinding expression not only to display the value of a data item, but also to update the value of a data item.
You create a template that supports two-way databinding expressions by creating a property that returns an object that implements the IBindableTemplate
interface. This interface inherits from the ITemplate
interface. It has the following two methods:
InstantiateIn
—. Instantiates the contents of a template in a particular control.
ExtractValues
—. Returns a collection of databinding expression values from a template.
For example, the ProductForm
control in Listing 30.7 represents a form for editing an existing product. The control includes a property named EditItemTemplate
that represents a two-way databinding template.
Example 30.7. ProductForm.cs
using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; using System.Collections.Specialized; namespace myControls { public class ProductForm : CompositeControl { public event EventHandler ProductUpdated; private IBindableTemplate _editItemTemplate; private ProductFormItem _item; private IOrderedDictionary _results; public IOrderedDictionary Results { get { return _results; } } public string Name { get { EnsureChildControls(); return _item.Name; } set { EnsureChildControls(); _item.Name = value; } } public decimal Price { get { EnsureChildControls(); return _item.Price; } set { EnsureChildControls(); _item.Price = value; } } [TemplateContainer(typeof(ProductFormItem), BindingDirection.TwoWay)] [PersistenceMode(PersistenceMode.InnerProperty)] public IBindableTemplate EditItemTemplate { get { return _editItemTemplate; } set { _editItemTemplate = value; } } protected override void CreateChildControls() { _item = new ProductFormItem(); _editItemTemplate.InstantiateIn(_item); Controls.Add(_item); } protected override bool OnBubbleEvent(object source, EventArgs args) { _results = _editItemTemplate.ExtractValues(_item); if (ProductUpdated != null) ProductUpdated(this, EventArgs.Empty); return true; } } public class ProductFormItem : WebControl, IDataItemContainer { private string _name; private decimal _price; public string Name { get { return _name; } set { _name = value; } } public decimal Price { get { return _price; } set { _price = value; } } public object DataItem { get { return this; } } public int DataItemIndex { get { return 0; } } public int DisplayIndex { get { return 0; } } } }
You should notice two special things about the EditItemTemplate
property. First, notice that the property returns an object that implements the IBindableTemplate
interface. Second, notice that the TemplateContainer
attribute that decorates the property includes a BindingDirection
parameter. You can assign one of two possible values to BindingDirection
: OneWay
and TwoWay
.
The ProductForm
includes an OnBubbleEvent()
method. This method is called when a child control of the ProductForm
control raises an event. For example, if someone clicks a Button
control contained in the EditItemTemplate
, the OnBubbleEvent()
method is called.
In Listing 30.7, the OnBubbleEvent()
method calls the EditItemTemplate's ExtractValues()
method. This method is supplied by the ASP.NET Framework because the EditItemTemplate
is marked as a two-way databinding template.
The ExtractValues()
method returns an OrderedDictionary
collection that contains name/value pairs that correspond to each of the databinding expressions contained in the EditItemTemplate
. The ProductForm
control exposes this collection of values with its Results
property. After the values are extracted, the control raises a ProductUpdated
event.
The page in Listing 30.8 illustrates how you can use the ProductForm
control to update the properties of a product.
Example 30.8. ShowProductForm.aspx
<%@ Page Language="C#" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> void Page_Load() { if (!Page.IsPostBack) { ProductForm1.Name = "Laptop"; ProductForm1.Price = 433.12m; ProductForm1.DataBind(); } } protected void ProductForm1_ProductUpdated(object sender, EventArgs e) { lblName.Text = ProductForm1.Results["Name"].ToString(); lblPrice.Text = ProductForm1.Results["Price"].ToString(); } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show ProductForm</title> </head> <body> <form id="form1" runat="server"> <div> <custom:ProductForm id="ProductForm1" Runat="server" OnProductUpdated="ProductForm1_ProductUpdated"> <EditItemTemplate> <asp:Label id="lblName" Text="Product Name:" AssociatedControlID="txtName" Runat="server" /> <asp:TextBox id="txtName" Text='<%# Bind("Name") %>' Runat="server" /> <br /><br /> <asp:Label id="lblPrice" Text="Product Price:" AssociatedControlID="txtPrice" Runat="server" /> <asp:TextBox id="txtPrice" Text='<%# Bind("Price") %>' Runat="server" /> <br /><br /> <asp:Button id="btnUpdate" Text="Update" Runat="server" /> </EditItemTemplate> </custom:ProductForm> <hr /> New Product Name: <asp:Label id="lblName" Runat="server" /> <br /><br /> New Product Price: <asp:Label id="lblPrice" Runat="server" /> </div> </form> </body> </html>
In the Page_Load()
method in Listing 30.8, the ProductForm Name
and Price
properties are set. Next, the DataBind()
is called in order to cause the ProductForm
control to evaluate its databinding expressions.
Notice that the ProductForm
control’s EditItemTemplate
includes Bind()
expressions instead of Eval()
expressions. You use Bind()
expressions in a two-way databinding template.
The EditItemTemplate
includes a Button
control. When you click the Button
control, the ProductForm
control’s OnBubbleEvent()
method executes, the values are retrieved from the EditItemTemplate
, and the ProductUpdated
event is raised.
The page in Listing 30.8 handles the ProductUpdated
event and displays the new values with two Label
controls (see Figure 30.3).
In this section, you learn how to build templated databound controls. A databound control can be bound to a DataSource
control such as the SqlDataSource
or ObjectDataSource
controls.
The ASP.NET Framework provides you with a number of base classes that you can use when creating a custom databound control. So, let’s look at some tables and figures. Table 30.1 lists the base control classes for all the standard ASP.NET databound controls. Figure 30.4 displays the inheritance hierarchy of all the new databound controls in the ASP.NET Framework. Typically, you inherit from one of the leaf nodes. You create a control that derives from the base CompositeDataBoundControl
, HierarchicalDataBoundControl
, or ListControl
class.
Table 30.1. Base Databound Control Classes
Control | Base Control |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
This chapter concentrates on inheriting new controls from the base CompositeDataBoundControl
class. This is the easiest base class to use when you want to display one or more database records and use templates.
You learned how to create controls that inherit from the base ListControl
class in Chapter 10, “Using List Controls.”
Let’s start simple. In this section, we create a custom databound control named the DivView
control. The DivView
control displays a set of data items (database records) in HTML <div>
tags.
The DivView
control inherits from the base CompositeDataBoundControl
class and overrides a single method of the base class. The DivView
control overrides the base class’s CreateChildControls()
method.
The DivView
control is contained in Listing 30.9.
Example 30.9. DivView.cs
using System; using System.Collections; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace AspNetUnleashed { public class DivView : CompositeDataBoundControl { private ITemplate _itemTemplate; [TemplateContainer(typeof(DivViewItem))] [PersistenceMode(PersistenceMode.InnerProperty)] public ITemplate ItemTemplate { get { return _itemTemplate; } set { _itemTemplate = value; } } protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding) { int counter = 0; foreach (object dataItem in dataSource) { DivViewItem contentItem = new DivViewItem(dataItem, counter); _itemTemplate.InstantiateIn(contentItem); Controls.Add(contentItem); counter++; } DataBind(false); return counter; } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Div; } } } public class DivViewItem : WebControl, IDataItemContainer { private object _dataItem; private int _index; public object DataItem { get { return _dataItem; } } public int DataItemIndex { get { return _index; } } public int DisplayIndex { get { return _index; } } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Div; } } public DivViewItem(object dataItem, int index) { _dataItem = dataItem; _index = index; } } }
The DivView
control supports an ItemTemplate
that is used to format each of its data items. You are required to supply an ItemTemplate
when you use the DivView
control.
All the work happens in the CreateChildControls()
method. Notice that this is not the same CreateChildControls()
method that is included in the base System.Web.UI.Control
class. The DivView
control overrides the CompositeDataBounControl
’s CreateChildControls()
method.
The CreateChildControls()
method accepts the following two parameters:
dataSource
—. Represents all the data items from the data source.
dataBinding
—. Represents whether the CreateChildControls()
method is called when the data items are being retrieved from the data source.
The CreateChildControls()
method is called every time that the DivView
control renders its data items. When the control is first bound to a DataSource
control, the dataSource
parameter represents the data items retrieved from the DataSource
control. After a postback, the dataSource
parameter contains a collection of null values, but the correct number of null values.
After a postback, the contents of the data items can be retrieved from View State. As long as the correct number of child controls is created, the Framework can rebuild the contents of the databound control.
You can use the dataBinding
parameter to determine whether the data items from the data source actually represent anything. Typically, the dataBinding
parameter has the value True
when the page first loads and the value False
after each postback.
Notice that the DataBind()
method is called after the child controls are created. You must call the DataBind()
method when a template includes databinding expressions. Otherwise, the databinding expressions are never evaluated.
The page in Listing 30.10 illustrates how you can bind the DivView
control to a SqlDataSource
control.
Example 30.10. ShowDivView.aspx
<%@ Page Language="C#" %> <%@ Register TagPrefix="custom" Namespace="AspNetUnleashed" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <style type="text/css"> .movies { width:500px; } .movies div { border:solid 1px black; padding:10px; margin:10px; } </style> <title>Show DivView</title> </head> <body> <form id="form1" runat="server"> <div> <custom:DivView id="lstMovies" DataSourceID="srcMovies" CssClass="movies" Runat="Server"> <ItemTemplate> <h1><%# Eval("Title") %></h1> Director: <%# Eval("Director") %> </ItemTemplate> </custom:DivView> <asp:SqlDataSource id="srcMovies" ConnectionString="<%$ ConnectionStrings:Movies %>" SelectCommand="SELECT Title, Director FROM Movies" Runat="server" /> <br /> <asp:LinkButton id="lnkReload" Text="Reload" Runat="server" /> </div> </form> </body> </html>
In Listing 30.10, the SqlDataSource
control represents the Movies database table. The DivView
control includes an ItemTemplate
that formats each of the columns from this database table (see Figure 30.5).
This chapter was devoted to the topic of building templated databound controls. In the first part, you learned how to support templates in your custom controls. You learned how to create templates that support both one-way and two-way databinding expressions. You also learned how to create a default template for a control.
The second half of this chapter focused on the topic of building databound controls. You learned how to create a simple DivView
control that displays the records from a database table in a series of HTML <div>
tags.