In particular, the class implements the InstantiateIn method, which creates all the controls that appear in the template.. When the DataBind method is called, the DataBinding event is ra
Trang 1container.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
Trang 2controls 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)
Trang 3Supporting 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
Trang 4Typically, 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
{
Trang 5_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;
}
}
Trang 6public 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>
Trang 7<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 datadata-binding
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
Trang 8LISTING 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;
Trang 9set
{
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; }
Trang 10set { _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