ptg 1654 CHAPTER 37 Building Templated Databound Controls 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 37.3 is similar to the control created in the previous section; however, the CreateChildControls() method has been modified. The new version of the CreateChildControls() method tests whether there is an ItemTemplate. If no ItemTemplate exists, an instance of the ArticleDefaultTemplate class is created. The ArticleDefaultTemplate class, which is also included in Listing 37.3, implements the ITemplate interface. In particular, the class implements the InstantiateIn() method, which creates all the controls that appear in the template. In Listing 37.3, three Label controls are created that correspond to the Title, Author, and Contents properties. The DataBinding event is handled for all three of these Label From the Library of Wow! eBook ptg 1655 Creating Templated Controls 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 37.4 illustrates how you can use the modified Article control. LISTING 37.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 37.4 does not include an ItemTemplate. When the page displays in a browser, the contents of the ItemTemplate are supplied by the ArticleDefaultTemplate class (see Figure 37.2). 37 From the Library of Wow! eBook ptg 1656 CHAPTER 37 Building Templated Databound Controls Supporting Simplified Databinding 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, 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. FIGURE 37.2 Displaying an article with a default template. From the Library of Wow! eBook ptg 1657 Creating Templated Controls 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 data- binding expression to refer to the value of a particular database record column. In this section, we create a nondatabound control that supports the simplified databind- ing syntax. The control is named the Product control, and is included in Listing 37.5. LISTING 37.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(); 37 From the Library of Wow! eBook ptg 1658 CHAPTER 37 Building Templated Databound Controls _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; } } From the Library of Wow! eBook ptg 1659 Creating Templated Controls public int DataItemIndex { get { return 0; } } public int DisplayIndex { get { return 0; } } } } The file in Listing 37.5 actually contains two classes: Product and ProductItem. The Product control includes an ItemTemplate property. 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, which 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 37.6 illustrates how you can use the Product control with the simpli- fied databinding syntax. LISTING 37.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” > 37 From the Library of Wow! eBook ptg 1660 CHAPTER 37 Building Templated Databound Controls <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”) displays 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. Supporting Two-Way Databinding Two-way databinding is a feature introduced with ASP.NET 2.0 Framework. Two-way data- binding 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 prop - erty that returns an object that implements the IBindableTemplate interface. This inter- face 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 37.7 represents a form for editing an existing product. The control includes a property named EditItemTemplate that repre- sents a two-way databinding template. From the Library of Wow! eBook ptg 1661 Creating Templated Controls LISTING 37.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; } 37 From the Library of Wow! eBook ptg 1662 CHAPTER 37 Building Templated Databound Controls 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; } From the Library of Wow! eBook ptg 1663 Creating Templated Controls set { _price = value; } } public object DataItem { get { return this; } } public int DataItemIndex { get { return 0; } } public int DisplayIndex { get { return 0; } } } } The EditItemTemplate property does two special things. First, the property returns an object that implements the IBindableTemplate interface. Second, 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, which 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 37.7, the OnBubbleEvent() method calls the EditItemTemplate’s ExtractValues() method. This method is supplied by 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 37.8 illustrates how you can use the ProductForm control to update the properties of a product. 37 From the Library of Wow! eBook . to ASP. NET developers. Supporting Two-Way Databinding Two-way databinding is a feature introduced with ASP. NET 2.0 Framework. Two-way data- binding enables you to extract values from a template Controls _item.Price = value; } } [TemplateContainer(typeof(ProductItem))] [PersistenceMode(PersistenceMode.InnerProperty)] public ITemplate ItemTemplate { get { return _itemTemplate; } set { _itemTemplate. myControls { public class ProductForm : CompositeControl { public event EventHandler ProductUpdated; private IBindableTemplate _editItemTemplate; private ProductFormItem _item; private IOrderedDictionary