我们知道,在开发ASP.NET 服务器控件时,ParseChildrenAttribute 类指示页分析器应如何处理页上声明的服务器控件标记中嵌套的内容,下边我引用MSDN的解释,然后用实例对其进行进一步的说明。
ParseChildrenAttribute 类允许您以 ParseChildrenAttribute 元数据属性标记服务器控件来为自定义服务器控件指定分析逻辑。
以元数据属性 (Attribute) ParseChildren(true) 标记服务器控件将指示分析器把包含在服务器控件标记内的元素解释为属性 (Property)。在这种情况下,ChildrenAsProperties 属性为 true。
以元数据属性 (Attribute) ParseChildren(true,"<Default Property>") 标记服务器控件将把 DefaultProperty 属性 (Property) 设置为传递到该属性 (Attribute) 的属性 (Property) 名称。
以元数据属性 ParseChildren(false)(默认值)标记服务器控件将指示分析器把包含在服务器控件标记中的元素解释为将通过关联的 ControlBuilder 进行分析的内容,即解释为控件。在这种情况下,ChildrenAsProperties 属性为 false。
如果我们仅仅看上面的解释,我相信大家都很郁闷。
针对ParseChildren(true,"<Default Property>") 这种情况,MSDN给出了一个例子:
http://msdn.microsoft.com/zh-cn/library/system.web.ui.parsechildrenattribute(VS.80).aspx
其中这段html

Code
<AspSample:CollectionPropertyControl id="CollectionPropertyControl1"
runat="server">
<AspSample:Employee Name="Employee 1"
Title="Title 1"
Alias="Alias 1" />
<AspSample:Employee Name="Employee 2"
Title="Title 2"
Alias="Alias 2" />
</AspSample:CollectionPropertyControl>
产生的临时代码如下(我做了一些整理,和实际的文件会有出入):

Code
1
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
2
private global::Samples.AspNet.CS.Controls.Employee @__BuildControl__control4()
{
3
global::Samples.AspNet.CS.Controls.Employee @__ctrl;
4
@__ctrl = new global::Samples.AspNet.CS.Controls.Employee();
5
@__ctrl.Name = "Employee 1";
6
@__ctrl.Title = "Title 1";
7
@__ctrl.Alias = "Alias 1";
8
return @__ctrl;
9
}
10
11
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
12
private global::Samples.AspNet.CS.Controls.Employee @__BuildControl__control5()
{
13
global::Samples.AspNet.CS.Controls.Employee @__ctrl;
14
@__ctrl = new global::Samples.AspNet.CS.Controls.Employee();
15
@__ctrl.Name = "Employee 2";
16
@__ctrl.Title = "Title 2";
17
@__ctrl.Alias = "Alias 2";
18
return @__ctrl;
19
}
20
21
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
22
private void @__BuildControl__control3(System.Collections.ArrayList @__ctrl)
{
23
global::Samples.AspNet.CS.Controls.Employee @__ctrl1;
24
@__ctrl1 = this.@__BuildControl__control4();
25
@__ctrl.Add(@__ctrl1);
26
27
global::Samples.AspNet.CS.Controls.Employee @__ctrl2;
28
@__ctrl2 = this.@__BuildControl__control5();
29
@__ctrl.Add(@__ctrl2);
30
}
31
32
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
33
private global::Samples.AspNet.CS.Controls.CollectionPropertyControl @__BuildControlCollectionPropertyControl1()
{
34
global::Samples.AspNet.CS.Controls.CollectionPropertyControl @__ctrl;
35
@__ctrl = new global::Samples.AspNet.CS.Controls.CollectionPropertyControl();
36
this.CollectionPropertyControl1 = @__ctrl;
37
@__ctrl.ID = "CollectionPropertyControl1";
38
this.@__BuildControl__control3(@__ctrl.Employees);
39
return @__ctrl;
40
}
可以看出,在应用了[ParseChildren(true, "Employees")]元数据属性,产生的Employee对象加入到一个ArrayList对象中,这个ArrayList对象又赋值给CollectionPropertyControl的Employees属性。
那么针对ParseChildren(false)这种情况,要实现同样的效果,我们应该怎么办呢?
MSDN告诉我们,这种情况由ControlBuilder来处理,我对MSDN提供的类文件做了一些修改:

Code
using System;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Security.Permissions;

namespace Samples.AspNet.CS.Controls


{
[AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal)]
public class MyControlBuilder : ControlBuilder

{

public override Type GetChildControlType(string tagName, IDictionary attribs)

{
// Allows TableRow without "runat=server" attribute to be added to the collection.
if (String.Compare(tagName, "AspSample:Employee", true) == 0)
return typeof(Employee);
return null;
}

public override void AppendLiteralString(string s)

{
// Ignores literals between rows.
}

}

// The child element class.
[AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal)]
public sealed class Employee

{
private String name;
private String title;
private String alias;


public Employee() : this("", "", "")
{ }

public Employee(String name, String title, String alias)

{
this.name = name;
this.title = title;
this.alias = alias;
}
public String Name

{
get

{
return name;
}
set

{
name = value;
}
}

public String Title

{
get

{
return title;
}
set

{
title = value;
}
}

public String Alias

{
get

{
return alias;
}
set

{
alias = value;
}
}
}
// Use the ParseChildren attribute to set the ChildrenAsProperties
// and DefaultProperty properties. Using this constructor, the
// control parses all child controls as properties and must define
// a public property named Employees, which it declares as
// an ArrayList. Nested (child) elements must correspond to
// child elements of the Employees property or to other
// properties of the control.
[ControlBuilderAttribute(typeof(MyControlBuilder))]
//[ParseChildren(true, "Employees")]
[AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal)]
public sealed class CollectionPropertyControl : Control

{
private String header;
private ArrayList employees = new ArrayList();

protected override void AddParsedSubObject(object obj)

{
Employee employ = obj as Employee;
if (employ != null)

{
employees.Add(employ);
}

}

public String Header

{
get

{
return header;
}
set

{
header = value;
}
}



public ArrayList Employees

{
get

{
return employees;
}
}
// Override the CreateChildControls method to
// add child controls to the Employees property when this
// custom control is requested from a page.
protected override void CreateChildControls()

{
Label label = new Label();
label.Text = Header;
label.BackColor = System.Drawing.Color.Beige;
label.ForeColor = System.Drawing.Color.Red;
Controls.Add(label);
Controls.Add(new LiteralControl("<BR> <BR>"));

Table table = new Table();
TableRow htr = new TableRow();

TableHeaderCell hcell1 = new TableHeaderCell();
hcell1.Text = "Name";
htr.Cells.Add(hcell1);

TableHeaderCell hcell2 = new TableHeaderCell();
hcell2.Text = "Title";
htr.Cells.Add(hcell2);

TableHeaderCell hcell3 = new TableHeaderCell();
hcell3.Text = "Alias";
htr.Cells.Add(hcell3);
table.Rows.Add(htr);

table.BorderWidth = 2;
table.BackColor = System.Drawing.Color.Beige;
table.ForeColor = System.Drawing.Color.Red;
foreach (Employee employee in Employees)

{
TableRow tr = new TableRow();

TableCell cell1 = new TableCell();
cell1.Text = employee.Name;
tr.Cells.Add(cell1);

TableCell cell2 = new TableCell();
cell2.Text = employee.Title;
tr.Cells.Add(cell2);

TableCell cell3 = new TableCell();
cell3.Text = employee.Alias;
tr.Cells.Add(cell3);

table.Rows.Add(tr);
}
Controls.Add(table);

}
}
}
对比两个类,可以看出,我添加了一个类
MyControlBuilder,同时对CollectionPropertyControl应用了属性[ControlBuilderAttribute(typeof(MyControlBuilder))],同时,在CollectionPropertyControl类中我们重写了AddParsedSubObject方法
上边的那段html,产生的临时代码如下:

Code
1
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
2
private global::Samples.AspNet.CS.Controls.Employee @__BuildControl__control3()
{
3
global::Samples.AspNet.CS.Controls.Employee @__ctrl;
4
@__ctrl = new global::Samples.AspNet.CS.Controls.Employee();
5
@__ctrl.Name = "Employee 1";
6
@__ctrl.Title = "Title 1";
7
@__ctrl.Alias = "Alias 1";
8
return @__ctrl;
9
}
10
11
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
12
private global::Samples.AspNet.CS.Controls.Employee @__BuildControl__control4()
{
13
global::Samples.AspNet.CS.Controls.Employee @__ctrl;
14
@__ctrl = new global::Samples.AspNet.CS.Controls.Employee();
15
@__ctrl.Name = "Employee 2";
16
@__ctrl.Title = "Title 2";
17
@__ctrl.Alias = "Alias 2";
18
return @__ctrl;
19
}
20
21
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
22
private global::Samples.AspNet.CS.Controls.CollectionPropertyControl @__BuildControlCollectionPropertyControl1()
{
23
global::Samples.AspNet.CS.Controls.CollectionPropertyControl @__ctrl;
24
@__ctrl = new global::Samples.AspNet.CS.Controls.CollectionPropertyControl();
25
this.CollectionPropertyControl1 = @__ctrl;
26
@__ctrl.ID = "CollectionPropertyControl1";
27
global::Samples.AspNet.CS.Controls.Employee @__ctrl1;
28
@__ctrl1 = this.@__BuildControl__control3();
29
System.Web.UI.IParserAccessor @__parser = ((System.Web.UI.IParserAccessor)(@__ctrl));
30
@__parser.AddParsedSubObject(@__ctrl1);
31
global::Samples.AspNet.CS.Controls.Employee @__ctrl2;
32
@__ctrl2 = this.@__BuildControl__control4();
33
@__parser.AddParsedSubObject(@__ctrl2);
34
return @__ctrl;
35
}
可以看出,在应用了
[ParseChildren(false]元数据属性,产生的Employee对象加入到CollectionPropertyControl的Controls集合中。在这个过程中,代码会分析添加进来的Control是否是Employee类型,如果是则添加到employees(ArrayList类型)集合中,我们重写AddParsedSubObject方法的目的就在于此。