我们知道,在开发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
Code
 <AspSample:CollectionPropertyControl id="CollectionPropertyControl1"
      <AspSample:CollectionPropertyControl id="CollectionPropertyControl1" 
 runat="server">
                                           runat="server">
 <AspSample:Employee Name="Employee 1"
        <AspSample:Employee Name="Employee 1" 
 Title="Title 1"
                            Title="Title 1" 
 Alias="Alias 1" />
                            Alias="Alias 1" />
 <AspSample:Employee Name="Employee 2"
        <AspSample:Employee Name="Employee 2" 
 Title="Title 2"
                            Title="Title 2" 
 Alias="Alias 2" />
                            Alias="Alias 2" />
 </AspSample:CollectionPropertyControl>
      </AspSample:CollectionPropertyControl>    产生的临时代码如下(我做了一些整理,和实际的文件会有出入):

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

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

 namespace Samples.AspNet.CS.Controls
namespace Samples.AspNet.CS.Controls


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

 
     {
{

 public override Type GetChildControlType(string tagName, IDictionary attribs)
        public override Type GetChildControlType(string tagName, IDictionary attribs)

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

 public override void AppendLiteralString(string s)
        public override void AppendLiteralString(string s)

 
         {
{
 // Ignores literals between rows.
            // Ignores literals between rows.
 }
        }

 }
    }

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

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


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

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

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

 
         {
{
 get
            get

 
             {
{
 return name;
                return name;
 }
            }
 set
            set

 
             {
{
 name = value;
                name = value;
 }
            }
 }
        }

 public String Title
        public String Title

 
         {
{
 get
            get

 
             {
{
 return title;
                return title;
 }
            }
 set
            set

 
             {
{
 title = value;
                title = value;
 }
            }
 }
        }

 public String Alias
        public String Alias

 
         {
{
 get
            get

 
             {
{
 return alias;
                return alias;
 }
            }
 set
            set

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

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

 protected override void AddParsedSubObject(object obj)
        protected override void AddParsedSubObject(object obj)

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

 
             {
{
 employees.Add(employ);
                employees.Add(employ);
 }
            }

 }
        }

 public String Header
        public String Header

 
         {
{
 get
            get

 
             {
{
 return header;
                return header;
 }
            }
 set
            set

 
             {
{
 header = value;
                header = value;
 }
            }
 }
        }



 public ArrayList Employees
        public ArrayList Employees

 
         {
{
 get
            get

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

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

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

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

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

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

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

 
             {
{
 TableRow tr = new TableRow();
                TableRow tr = new TableRow();

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

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

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

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

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

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