您的位置:首页 > 编程语言 > ASP

ASP.NET自定义控件复杂属性声明持久性浅析

2008-05-10 00:26 471 查看
ASP.NET自定义控件复杂属性声明持久性浅析

在自定义控件的开发过程中,我们经常要给控件添加一些复杂类型的属性。利用声明持久性(Declarative Persistence)可使得页面开发人员能够让页面开发人员在ASP.NET页面中,声明性地设置这些复杂属性值,而无需编写任何C#或者VB.NET代码。

参见下面的例子:

GridView的DataKeyNames属性,其数据类型是string[]:
<asp:GridView ID="GridView1" runat="server" DataKeyNames="ID, Title, Author">

</asp:GridView>

GridView的RowStyle属性,其数据类型是System.Web.UI.WebControls.TableItemStyle:

<asp:GridView ID="GridView1" runat="server">

<RowStyle BackColor="Red" ForeColor="Black"/>

</asp:GridView>

GridView的Columns属性,其数据类型是:System.Web.UI.WebControls.DataControlFieldCollection

<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1">

<Columns>

<asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />

<asp:BoundField DataField="ID" HeaderText="ID" SortExpression="ID" />

</Columns>

</asp:GridView>

GridView的PagerTemplate属性, 其数据类型是System.Web.UI.ITemplate:

<asp:GridView ID="GridView1" runat="server">

<PagerTemplate>

<div>

<span>Pager Template</span>

</div>

</PagerTemplate>

</asp:GridView>

那如何才能实现在ASPX中声明性地设置这些复杂属性哪?

下面我将逐一讲述这些属性背后的故事,本文的重点不在于如何维护这些属性的状态,而是如何由ASPX Markup到复杂属性的构建。

一、由ASPX Markup 到C# 或者VB.NET class

1.ASP.NET管道

<httpHandlers>

<add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True"/>

<add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True"/>

</httpHandlers>

[b]2.PageHandlerFactory


PageHandlerFactory负责找到包含请求页面类的程序集,如果该程序集还没有被创建,则即时动态创建。请求页面类是通过解析ASPX资源的Markup代码创建的,并且存放在ASP.NET的临时文件夹%AppData%"Local"Temp"Temporary
ASP.NET Files中。

3.ControlBuilder

而ControlBuilder类就是负责将ASPX Markup声明解析成为ASP.NET Server控件,通常页面上的每个控件都有一个默认的 ControlBuilder
类相关联。在ASPX页面解析过程中,ASP.NET 页框架首先会生成与页面控件树对应的
ControlBuilder 对象树,然后 ControlBuilder 树用于生成页代码并创建控件树。

ControlBuilder 定义了如何解析控件标记中的内容的,我们可以通过自定义ControlBuilder类来重写此默认行为。

在页面解析过程中,ControlBuilder将会检查ASP.NET Server控件是否标示了ParseChildren(true)
Attribute。如果被标示则该控件内部嵌套的子节点将被解析为控件的子属性,否则该节点会被解析为ASP.NET
Server控件,并添加到原控件的Controls集合,关于该Attribute见下一节。

二、相关的Attributes

1.ParseChildrenAttribute

ParseChildrenAttribute应用于自定义控件类上,该Attribute将会告诉ASPX页面解析器如何解析自定义控件内部的嵌套节点。

下表详细的描述了ParseChildrenAttribute的用法:

Attribute Usage

描述

ParseChildren(true)

嵌套的子节点必须对应着当前控件的属性,如果找不到对应属性将会产生一个解析错误。另外在当前控件的Tag内部也不允许任何文字节点。

例子:Repeater 以及其他数据绑定控件。

ParseChildrenAttribute(true,
"PropertyName")

当前控件必须包含一个Public的属性,属性名等同于参数PropertyName。该属性应该是一个集合类的数据类型。

而嵌套的字节点必须对应着该属性的子Element.

例子:HtmlTable, HtmlTableRow控件。

ParseChildrenAttribute(false)

ParseChildrenAttribute(false,
"PropertyName")

ParseChildrenAttribute is not applied to
the control.

嵌套的子节点必须是ASP.NET 服务器控件。页面解析器会根据该节点创建一个子控件,然后在当前控件上调用IParserAccessor.AddParsedSubObject方法,该方法的默认实现是将解析到的子控件添加到当前控件的Controls集合。

任何Literal文字节点将被创建为LiteralControl的示例。

例子:Panel控件。

ParseChildrenAttribute (Type childControlType)

嵌套的子节点必须是指定的ASP.NET 服务器控件类型。

例子:MultiView控件。

WebControl类上已经被ParseChildrenAttribute(True)标示了,所以每个直接或者间接从WebControl派生的控件都会默认支持内部属性声明持久性。

在下面的例子中RowStyle节点将被解析为GridView控件的子属性:

<asp:GridView ID="GridView1" runat="server">

<RowStyle BackColor="Red" ForeColor="Black"/>

</asp:GridView>
ASP.NET Server控件可以通过ControlBuilderAttribute来指定特定的ControlBuilder,来修改上述的解析逻辑。

2. PersistChildrenAttribute

PersistChildrenAttribute应用于自定义控件类上,是一个DesignTime的Attribute。用于指定是否将自定义控件内部的嵌套节点解析为子控件,True将意味着解析该节点为控件。

WebControl类上已经被PersistChildrenAttribute (False)标示了,而Panel类则被PersistChildrenAttribute (True)标示。

示例:

[ParseChildren(false), PersistChildren(true)]

public class MyControl : WebControl

在设计时添加一个Button控件到MyControl中:

<cc1:MyControl2 ID="MyControl21" runat="server" BorderStyle="Dotted" Height="56px" Width="349px">

<asp:Button ID="Button2" runat="server" Text="Button" />

</cc1:MyControl2>

这个时候用户可以在VS IDE的Design View中选中子Button控件,而如果MyControl被PersistChildrenAttribute (False)标示的话,子控件Button不能被选中。

3. PersistenceModeAttribute

PersistenceModeAttribute应用在ASP.NET 服务器控件属性上,是一个DesignTime的Attribute,用于指定用如何在设计时将ASP.NET Server控件属性(Property)保存到ASP.NET 页面

或者说在 ASPX Mrakup中以何种方式声明该属性。

PersistenceMode.InnerProperty则指定属性在 ASP.NET 服务器控件中保持为嵌套标记。

三、再看例子

1. GridView的DataKeyNames属性,其数据类型是string[]:

<asp:GridView ID="GridView1" runat="server" DataKeyNames="ID, Title, Author">

</asp:GridView>

默认情况下ASP.NET页面解析引擎会将ASPX Markup中赋予DataKeyNames属性的值直接设置到该属性上,由于该属性的数据类型为string[],直接设置将会失败。

那GridView做了些什么哪,见下面的代码片断:

[TypeConverter(typeof(StringArrayConverter))]

public virtual string[] DataKeyNames

<asp:GridView ID="GridView1" runat="server">

<RowStyle BackColor="Red" ForeColor="Black"/>

</asp:GridView>
由于GridView控件已经标示了ParseChildrenAttribute(True),所以其内部嵌套的节点将被解析为自身的属性。

另外RowStyle属性也添加了PersistenceModeAttribute来指定如何生成ASPX Markup代码。

[PersistenceMode(PersistenceMode.InnerProperty)]

public TableItemStyle RowStyle

<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1">

<Columns>

<asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />

<asp:BoundField DataField="ID" HeaderText="ID" SortExpression="ID" />

</Columns>

</asp:GridView>

ASP.NET页面是如何来解析上面的一段代码哪?首先Columns节点将被解析为GridView的属性,可是Columns内部的子节点是如何被解析并添加到Columns集合中去的哪?

另外在VS IDE Source View中准备添加Columns属性的子节点代码的时候,为什么VS IDE会帮我们列出所有可实例化的子类型?

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

2private global::System.Web.UI.WebControls.BoundField @__BuildControl__control23()

3

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

private void @__BuildControl__control22(System.Web.UI.WebControls.DataControlFieldCollection @__ctrl)

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

private global::System.Web.UI.WebControls.GridView @__BuildControlGridView1()

{

global::System.Web.UI.WebControls.GridView @__ctrl;

@__ctrl = new global::System.Web.UI.WebControls.GridView();

this.GridView1 = @__ctrl;

@__ctrl.ApplyStyleSheetSkin(this);

@__ctrl.ID = "GridView1";

this.@__BuildControl__control22(@__ctrl.Columns);

return @__ctrl;

}

参照上面说的几点我们就可以写出自定义的集合类属性的声明持久化。

当然了这只是实现了ASPX到CS,我们还需要给集合类以及子Element添加关于状态管理的代码(实现IStateManager接口)才可以在实际项目中使用,这一点我就不再赘述了。

4. GridView的PagerTemplate属性,其数据类型是ITemplate:

其实ITemplate节点内部就是一个控件集合,TemplateBuilder负责将该Tag构建成为一个控件组。

在运行时我们通过ITemplate接口的InstantiateIn方法将一个实例化的Template放到指定的Container中去。

全文完。

已接近深夜,有不妥的地方下来再改。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐