Master Page 的原理分析, IParserAccessor.AddParsedSubObject 方法
当一个 ASP.NET 页面的请求发生时,Page 对象初始化的次序是
constructor -> AddParsedSubObject -> ...
可见 AddParsedSubObject 这个方法会较早被调用。
System.Web.UI.Control 类实现了 IParserAccessor 接口,
它对此接口实现如下:(通过 Reflector 看到的)
我们可以看到 Control 类给 AddParsedSubObject 方法提供了一个默认实现,默认的行为就是简单的判断如果该对象存在则添加到自己的子控件集合中去。
同时他设定访问性级别为 virtual,给继承自他的子类(我们要编写的自定义控件)以重写该方法的实现的机会。
在某些高级的页面模版解决方案中,通常覆盖这个方法,把分析出来的页面的控件添加到某个特定的页面模版中去。
Paul Wilson 的 MasterPage 控件里这个部分是这样写的:
下面看一下 msdn 对该接口的解释。
IParserAccessor 接口只规定了一个方法:
这个方法的目的是:(中文 msdn 这个方法的翻译很烂,郁闷了很久之后翻看原文的才看懂了)。
通知服务器控件,它的一个元素(XML 或 HTML) 已经分析完毕了;并且把这个元素添加到服务器控件的 ControlCollection 对象中去。
参数 obj: 已分析的 Object.
注:
除非你覆盖它,这个方法会自动的向控件的 ControlCollection 集合(通过 Control.Controls 来访问)添加一些 LiteralControl.(实际上是 tag 间隔处的空白)。
msdn 例子:
这个代码演示了如何把自定义的子 tag (例子中叫做 "myitem") 当作 TextBox 来处理。
题外话:
刚才不小心搜到孟子E章在 csdn 文档中心写的一个关于 ASP.NET 1.x 和 2.0 对照的文章,里面提到 IParserAccessor 接口的以及这个方法好像被微软封掉了。以后只能是系统才有权调用了。
虽然说 ASP.NET 2.0 加入了 MasterPage 机制,但这样做搞的 1.1 的代码不兼容了岂不是很霸道?哪位清楚详情的请证实一下这个事情。
参考:
Master Page, Paul Wilson 相关:http://authors.aspalliance.com/paulwilson/Articles/?id=14
-->当一个 ASP.NET 页面的请求发生时,Page 对象初始化的次序是
constructor -> AddParsedSubObject -> ...
可见 AddParsedSubObject 这个方法会较早被调用。
System.Web.UI.Control 类实现了 IParserAccessor 接口,
它对此接口实现如下:(通过 Reflector 看到的)
void IParserAccessor.AddParsedSubObject(object obj)
{
this.AddParsedSubObject(obj);
}
protected virtual void AddParsedSubObject(object obj)
{
Control control1 = obj as Control;
if (control1 != null)
{
this.Controls.Add(control1);
}
}
{
this.AddParsedSubObject(obj);
}
protected virtual void AddParsedSubObject(object obj)
{
Control control1 = obj as Control;
if (control1 != null)
{
this.Controls.Add(control1);
}
}
我们可以看到 Control 类给 AddParsedSubObject 方法提供了一个默认实现,默认的行为就是简单的判断如果该对象存在则添加到自己的子控件集合中去。
同时他设定访问性级别为 virtual,给继承自他的子类(我们要编写的自定义控件)以重写该方法的实现的机会。
在某些高级的页面模版解决方案中,通常覆盖这个方法,把分析出来的页面的控件添加到某个特定的页面模版中去。
Paul Wilson 的 MasterPage 控件里这个部分是这样写的:
protected override void AddParsedSubObject(object obj) {
if (obj is Wilson.MasterPages.ContentRegion) {
this.contents.Add(obj);
}
else {
this.defaults.Controls.Add((Control)obj);
}
}
if (obj is Wilson.MasterPages.ContentRegion) {
this.contents.Add(obj);
}
else {
this.defaults.Controls.Add((Control)obj);
}
}
下面看一下 msdn 对该接口的解释。
IParserAccessor 接口只规定了一个方法:
void AddParsedSubObject(object obj);
这个方法的目的是:(中文 msdn 这个方法的翻译很烂,郁闷了很久之后翻看原文的才看懂了)。
通知服务器控件,它的一个元素(XML 或 HTML) 已经分析完毕了;并且把这个元素添加到服务器控件的 ControlCollection 对象中去。
参数 obj: 已分析的 Object.
注:
除非你覆盖它,这个方法会自动的向控件的 ControlCollection 集合(通过 Control.Controls 来访问)添加一些 LiteralControl.(实际上是 tag 间隔处的空白)。
msdn 例子:
这个代码演示了如何把自定义的子 tag (例子中叫做 "myitem") 当作 TextBox 来处理。
// Custom ControlBuilder class. Interprets nested tag name "myitem" as a textbox.
public class MyControlBuilder : ControlBuilder
{
public override Type GetChildControlType(String tagName,
IDictionary attributes)
{
if (String.Compare(tagName, "myitem", true) == 0)
{
return typeof(TextBox);
}
return null;
}
}
[
ControlBuilderAttribute(typeof(MyControlBuilder))
]
public class MyControl : Control
{
// Store all the controls specified as nested tags.
private ArrayList items = new ArrayList();
// This function is internally invoked by IParserAccessor.AddParsedSubObject(Object).
protected override void AddParsedSubObject(Object obj)
{
if (obj is TextBox)
{
items.Add(obj);
}
}
// Override 'CreateChildControls'.
protected override void CreateChildControls()
{
System.Collections.IEnumerator myEnumerator = items.GetEnumerator();
while(myEnumerator.MoveNext())
this.Controls.Add((TextBox)myEnumerator.Current);
}
}
public class MyControlBuilder : ControlBuilder
{
public override Type GetChildControlType(String tagName,
IDictionary attributes)
{
if (String.Compare(tagName, "myitem", true) == 0)
{
return typeof(TextBox);
}
return null;
}
}
[
ControlBuilderAttribute(typeof(MyControlBuilder))
]
public class MyControl : Control
{
// Store all the controls specified as nested tags.
private ArrayList items = new ArrayList();
// This function is internally invoked by IParserAccessor.AddParsedSubObject(Object).
protected override void AddParsedSubObject(Object obj)
{
if (obj is TextBox)
{
items.Add(obj);
}
}
// Override 'CreateChildControls'.
protected override void CreateChildControls()
{
System.Collections.IEnumerator myEnumerator = items.GetEnumerator();
while(myEnumerator.MoveNext())
this.Controls.Add((TextBox)myEnumerator.Current);
}
}
题外话:
刚才不小心搜到孟子E章在 csdn 文档中心写的一个关于 ASP.NET 1.x 和 2.0 对照的文章,里面提到 IParserAccessor 接口的以及这个方法好像被微软封掉了。以后只能是系统才有权调用了。
虽然说 ASP.NET 2.0 加入了 MasterPage 机制,但这样做搞的 1.1 的代码不兼容了岂不是很霸道?哪位清楚详情的请证实一下这个事情。
参考:
Master Page, Paul Wilson 相关:http://authors.aspalliance.com/paulwilson/Articles/?id=14
Feedback
否则的话属于普通的 Control, 那就加到一个默认 Slot 里面去.(default ContentRegion). 该方法里还没有涉及添加哪些控件到自身 Controls 集合里去.
接下来执行的动作是:
BuildMasterPage 私有方法.
该方法完成了模板 Slot 及其预定义默认内容的复制过程.
先用 LoadControl 方法加载存储模板的那个 UserControl;
把其中的所有子控件去除掉,同时判断如果是可见的则添加到自身(主页面对象)的 Controls 集合.
最后把模板 UserControl 本身(一个空壳)加到 Controls 集合的最开始处.
最后的 BuildContents 私有方法里,
将主页面里定义的 Slot 的所有实现替代到从模板里复制来的 Slot 里面.(具体实现).