在Asp.net 2.0以后的版本,Asp.net提供了服务器控件模板(Template)和数据绑定(Data Bind)来简化开发工作,模板是是用于定制化服务器控件或者HTML如何在页面呈现,而模板和数据绑定往往结合起来在一起实现更高级的功能,比如最经典的GridView.例如,在 GridView服务器控件中可以使用 HTML 元素和控件的组合来创建列表中每行的布局。同样,GridView服务器控件对网格中的每行都具有一个默认的外观。但是,您可以通过为单个行、间隔行、所选行等行定义不同的模板来自定义网格的外观。
定制控件内容
模板用于让开发人员自定义HTML或者服务器控件作为主要控件输出流的一部分。提供了模板的服务器控件其实是给予插入自定义内容提供了容器。
服务器控件模板的强大之处在于它通过让开发人员可以定制输出特定的html来给予了开发人员极高的灵活性.如下图:
使用服务器控件模板
使用服务器控件模板的一大好处是我们可以专注开发空间,而把外观等html和css设置内容让其他人来完成。
在GridView控件里,我们可以通过在ItemTemplate里设置任何我们想设置的内容,在DropDownList控件中我们可以插入ListItem子控件,但在里面插入比如TextBox控件则不行。这个原因就要说到下面一个标签(Attribute)
ParseChildren Attribute
服务器控件必须通过在类声明时添加ParseChildren标签来告诉asp.net页面分析器这个控件需要支持模板。ParseChildren的功能是让服务器控件所含有的子控件作为它的一个属性存在。
对于继承System.Web.UI.WebControls.WebControl基类的控件,这个标签已经通过继承而存在不需要再声明
ParseChildren标签还暴露了ChildrenAsProperties属性,在使用
可以:ParseChildrenAttribute(ChildrenAsProperties = true)
也可以用简便写法:ParseChildren(true)
ChildrenAsProperty属性的作用是让控件的属性和直接在控件内部的html代码,或者说是XML代码(“<”和”>”)进行匹配.如下图:
而不使用ChildrenAsProperties属性的则会是如下图:
下面通过一个Demo来看
Demo 服务器导航菜单
先看Demo的效果
先声明两个用于存放子控件的容器,代码如下:
public class BasicTemplateContainer : WebControl, INamingContainer
{
public BasicTemplateContainer(): base(HtmlTextWriterTag.Div)
{
this.BorderWidth = 1;
this.BorderStyle = BorderStyle.Solid;
}
}
public class SeperatorTemplateContainer : WebControl, INamingContainer
{
public SeperatorTemplateContainer(): base(HtmlTextWriterTag.Span)
{
}
}
第一个用于存放HeaderTemplate和footerTemplate,而第二个用于存放分隔符
再声明一个存放菜单超链接的容器,代码如下:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class MenuItemData
{
public MenuItemData()
{
}
[NotifyParentProperty(true)]
public string Title { get; set; }
[NotifyParentProperty(true)]
public string Url { get; set; }
[NotifyParentProperty(true)]
public string ImageUrl { get; set; }
[NotifyParentProperty(true)]
public string Target { get; set; }
}
最后声明一个继承于CompositeControl基类的控件,声明代码如下:
public class TemplateMenu : CompositeControl
最终完全代码如下:
TemplateMenu完全代码
1 using System;
2 using System.Web;
3 using System.Web.UI;
4 using System.Web.UI.WebControls;
5 using System.Collections;
6 using System.ComponentModel;
7 using System.Web.UI.WebControls;
8 namespace bindcontrol
9 {
10 [ToolboxData("<{0}:templatemenu runat=server></{0}:templatemenu>")]
11 public class TemplateMenu : CompositeControl
12 {
13 private ArrayList menuData;
14 public TemplateMenu(): base()
15 {
16 menuData = new ArrayList(){
17
18 new MenuItemData{Title="博客园", Url="http://www.cnblogs.com"},
19 new MenuItemData{Title="Microsoft", Url="http://www.microsoft.com"},
20 new MenuItemData{Title="ASP.Net", Url="http://asp.net"}};
21 }
22 private ITemplate headerTemplate;
23 [Browsable(false), Description("The header template"),
24 PersistenceMode(PersistenceMode.InnerProperty),
25 TemplateContainer(typeof(BasicTemplateContainer))]
26 public ITemplate HeaderTemplate
27 {
28 get
29 {
30 return headerTemplate;
31 }
32 set
33 {
34 headerTemplate = value;
35 }
36 }
37 private ITemplate footerTemplate;
38 [Browsable(false), Description("The footer template"),
39 PersistenceMode(PersistenceMode.InnerProperty),
40 TemplateContainer(typeof(BasicTemplateContainer))]
41 public ITemplate FooterTemplate
42 {
43 get
44 {
45 return footerTemplate;
46 }
47 set
48 {
49 footerTemplate = value;
50 }
51 }
52 private ITemplate separatorTemplate;
53 [Browsable(false), Description("The separator template"),
54 PersistenceMode(PersistenceMode.InnerProperty),
55 TemplateContainer(typeof(SeperatorTemplateContainer))]
56 public ITemplate SeparatorTemplate
57 {
58 get
59 {
60 return separatorTemplate;
61 }
62 set
63 {
64 separatorTemplate = value;
65 }
66 }
67 private void CreateControlHierarchy()
68 {
69 if (HeaderTemplate != null)
70 {
71 BasicTemplateContainer header = new BasicTemplateContainer();
72 HeaderTemplate.InstantiateIn(header);
73 Controls.Add(header);
74 }
75 int count = menuData.Count;
76 for (int index = 0; index < count; index++)
77 {
78 MenuItemData itemdata = (MenuItemData)menuData[index];
79 HyperLink link = new HyperLink()
80 {
81 Text = itemdata.Title,
82 NavigateUrl = itemdata.Url,
83 ImageUrl = itemdata.ImageUrl,
84 Target = itemdata.Target
85 };
86 Controls.Add(link);
87 if (index != count - 1)
88 {
89 if (SeparatorTemplate != null)
90 {
91 SeperatorTemplateContainer separator = new SeperatorTemplateContainer();
92 SeparatorTemplate.InstantiateIn(separator);
93 Controls.Add(separator);
94 }
95 else
96 {
97 Controls.Add(new LiteralControl(" | "));
98 }
99 }
100 }
101 if (FooterTemplate != null)
102 {
103 BasicTemplateContainer footer = new BasicTemplateContainer();
104 FooterTemplate.InstantiateIn(footer);
105 Controls.Add(footer);
106 }
107 }
108 override protected void CreateChildControls()
109 {
110 Controls.Clear();
111 CreateControlHierarchy();
112 }
113 public override ControlCollection Controls
114 {
115 get
116 {
117 EnsureChildControls();
118 return base.Controls;
119 }
120 }
121 }
122
123 public class BasicTemplateContainer : WebControl, INamingContainer
124 {
125 public BasicTemplateContainer(): base(HtmlTextWriterTag.Div)
126 {
127 this.BorderWidth = 1;
128 this.BorderStyle = BorderStyle.Solid;
129 }
130 }
131 public class SeperatorTemplateContainer : WebControl, INamingContainer
132 {
133 public SeperatorTemplateContainer(): base(HtmlTextWriterTag.Span)
134 {
135 }
136 }
137 [TypeConverter(typeof(ExpandableObjectConverter))]
138 public class MenuItemData
139 {
140 public MenuItemData()
141 {
142 }
143 [NotifyParentProperty(true)]
144 public string Title { get; set; }
145 [NotifyParentProperty(true)]
146 public string Url { get; set; }
147 [NotifyParentProperty(true)]
148 public string ImageUrl { get; set; }
149 [NotifyParentProperty(true)]
150 public string Target { get; set; }
151 }
152 }
前台调用代码如下:
首先注册控件:<%@ Register Namespace="bindcontrol" TagPrefix="dd" %>
然后是前台代码:
<dd:TemplateMenu runat="server" >
<HeaderTemplate>template header</HeaderTemplate>
<SeparatorTemplate>%</SeparatorTemplate>
<FooterTemplate>template footer</FooterTemplate>
</dd:TemplateMenu>
注意,作为模板的类型必须声明成ITemplate类型,而这个ITemplate的具体类型则通过TemplateContainer标签进行注入.我们通过声明CreateControlHierarchy()函数来进行控制控件的具体输出,最后通过覆盖父类的CreateChildControls()方法来调用我们写好的CreateControlHierarchy方法达到控制输出的目的。
最后,你可能有疑问,那个神奇的ChildrenAsProperties属性跑哪去了?如果没有这个属性,那上面<headerTemplate>之类的标签又是如何匹配的呢?还记得吗,继承与WebControl基类的控件继承了这个标签,所以不用显示声明,所以ChildrenAsProperties属性come for free:-)