這次我們繼續討論.主題是模板控件,模板控件將是復雜控件的起步
1.asp.net內置的模板控件,了解模板控件
如下圖,以下為asp.net內置的模板控件

上圖的控件一方面是模板控件,另一方面又是數據綁定控件.這里我們暫且不討論如何實現數據綁定。使用上面控件的話,應該熟悉控件存在著不同的模板,如下圖Repeater控件的模板類型。

在不同模板內你可以定義控件顯示內容會呈現不同效果.典型的運用就是GridView,其呈現代碼會是一個表格代碼,而Repeater則是自定義的.其實其是內部已經實現了的,暫且先不管這些.下面一步步看下來如何實現.
2.實現模板控件
2.1簡單實現模板控件(靜態模板)
(1)模板控件為特殊的復合控件,你還是需要實現INamingContainer接口,因為在模板屬性的內容是為子控件集合添加到模板控件中,為保證控件具有唯一標識符.其實現將在CreateChildControls方法中創建子控件。asp.net2.0中可以直接繼續CompositeControl就可。
(2)定義控件屬性
模板屬性為System.Web.UI.ITemplate 接口,此接口有一InstantiateIn 方法 將在下面分析。上一篇我們說明了控件內部屬性和控件的區別,模板并非控件而是屬性,我們在屬性瀏覽器中并未看到此屬性,是因為我們為其加了元數據,作為內部屬性使用。定義模板屬性方法如下
//聲明變量
private ITemplate _itemTemplate;


//屬性
[Browsable(false)]
[TemplateContainer(typeof(Article))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ItemTemplate

{

get
{ return _itemTemplate; }

set
{ _itemTemplate = value; }
}
這里我們認識到了一個TemplateContainer元數據,其與容器控件關聯起來.Article為默認其自身控件,即默認將自身控件作為容器控件.
(3).重寫CreateChildControls方法
此方法我們以前已認識過了,主要是為控件添加子控件
protected override void CreateChildControls()

{
_itemTemplate.InstantiateIn(this);
}
這次我們要做的重點是認識ITemplate接口的InstantiateIn 方法,方法有一個Control參數,其為子控件和模板定義了一個容器控件(此處為其自身控件,下面看頁面代碼).如GridView和DataList控件都實現了自定義的容器控件.Repeater則是完全自定義的.這里暫且默認實現。
實現代碼:在模板內拖了一個label控件
<custom:Article
id="Article1"
Runat="server">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</ItemTemplate>
</custom:Article>
OK,你可以看一下效果了,當然你可以定義多個模板然后在多個不同模板內添加內容.我們來看下其控件樹內容,如下圖

子控件有一個Label控件,非控件內容則以LiteralControl呈現.
2.2實現動態模板
當我們使用DataList控件時,往往在模板中動態的綁定一些數據,獲取的這些數據則是ITemplate接口的InstantiateIn 方法中的容器控件.下面我們為控件定義屬性,然后通過DataBind()方法和數據綁定表達式獲取數據。我們先先定義三個屬性
頁面代碼,注意要用DataBind()方法
void Page_Load()

{
Article1.Title = "Creating Templated Databound Controls";
Article1.Author = "Stephen Walther";
Article1.Contents = "Blah, blah, blah, blah
";
Article1.DataBind();
}
通過Container數據綁定表達式獲取容器對象屬性,此處容器對象為默認的Article

如下實現
<custom:Article
id="Article1"
Runat="server">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

<%
# Container.Title%><br />

<%
# Container.Author %><br />

<%
# Container.Contents %><br />
</ItemTemplate>
</custom:Article>
好了,到這里你就實現了一個簡單的動態模板控件了.
2.3實現默認模板
在購書網站上我們常常看到由于圖書太多的情況下,管理人員未能將圖書封面發布到網站上,這時此書可能出現默認的圖片"尚為此書添加圖書封面"。在一個具有模板的控件里,如果你未為控件添加模板屬性的話,你可以通過實現默認模板來實現默認效果。
(1)那你第一步要做的就是定義一個自定義模板.此模板需要實現ITemplate接口,實現InstantiateIn方法.看一下典型實現,如下代碼
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;
}

}
在InstantiateIn方法中,定義了默認控件,并實現了
默認綁定.在各自的數據綁定事件里通過容器控件(
默認容器控件為ArticleWithDefault,此處還是沒自定義容器控件,下面會介紹)的NamingContainer屬性獲取控件ID值.然后對控件進行賦值。
(2)重寫CreateChildControls方法
當未定義模板屬性時,則實現默認模板
protected override void CreateChildControls()

{
if (_itemTemplate == null)
_itemTemplate = new ArticleDefaultTemplate();
_itemTemplate.InstantiateIn(this);
}
(3)頁面代碼
下面實現效果跟2.2的定義的模板控件效果一樣,這里只為說明默認模板的使用方法
void Page_Load()
{
ArticleWithDefault1.Title = "Creating Templated Databound Controls";
ArticleWithDefault1.Author = "Stephen Walther";
ArticleWithDefault1.Contents = "Blah, blah, blah, blah
";
ArticleWithDefault1.DataBind();
}

<custom:ArticleWithDefault
id="ArticleWithDefault1"
Runat="server" />
2.4實現自定義容器控件
上面我已經多次注明容器控件為默認自身控件,你可以通過自定義容器控件。GridView控件會自動把數據以表格形式呈現,DataList控件有DataListItem ,Repeater則有RepeaterItem。這些控件實現數據綁定后,通常不是顯示一條數據的,其控件都有一個Items屬性,其表示項集合。
每項數據都在其Item里面,看一下DataList綁定數據以后的控件樹

我們常常會需要在模板控件里以以下方式來獲取模板內部控件,如在DataList控件中
protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)

{
e.Item.FindControl("");
DataList1.Items[0].BackColor = System.Drawing.Color.Red;
}
通過此方法我們可以處理一些特殊的列和行.為實現上面效果,我們也可以為模板控件自定義容器控件
(1)自定義容器控件類
注意需要實現IDataItemContainer接口,就如DataList一樣,其綁定的數據不可能是一條的.
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; }
}
}
然后在主控件中如下實現
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;
}
}
(2)用TemplateContainer與模板屬性關聯起來
[TemplateContainer(typeof(ProductItem))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ItemTemplate

{

get
{ return _itemTemplate; }

set
{ _itemTemplate = value; }
}
(3)重寫CreateChildControls方法
注意了,此處模板的InstantiateIn方法不再是this了,而是自定義容器控件了,再用數據綁定表達式訪問的將是ProductItem的數據(即自定義容器控件的數據)
protected override void CreateChildControls()

{
_item = new ProductItem();
_itemTemplate.InstantiateIn(_item);
Controls.Add(_item);
}
(4)頁面代碼
void Page_Load()

{
Product1.Name = "Laptop Computer";
Product1.Price = 1254.12m;
Product1.DataBind();
}

<custom:Product
id="Product1"
Runat="Server">
<ItemTemplate>
Name: <%# Eval("Name") %>
<br />
Price: <%# Eval("Price", "{0:c}") %>
</ItemTemplate>
</custom:Product>
上面以Eval來綁定數據,也可以用Container表達式,如下圖,其類型為ProductItem
注意:當不是數據綁定控件時,則不能用Eval綁定語法,如上面的幾個例子.大家可以測試一下。
上一篇:asp.net控件開發基礎(15)
下一篇:asp.net控件開發基礎(17)