服务器控件是否必须在 runat=server 的 Form 内?
对这个概念很多人有误解。以为凡是 WebControl 都必须放在 <form runat="server"></form> 里面。比如:http://community.csdn.net/Expert/topic/3848/3848189.xml?temp=.1163141
先让我们来做几个试验。
(为了说明方便,以下仅列出 aspx 文件中 body 里的内容。)
1,
2,
3,
结果发现了什么?
Label 可以正常使用。而 TextBox 和 Button 都会出现抛出一个异常。
因为 Label, Button, TextBox 都继承自 System.Web.UI.WebControls.WebControl 类。下面我们用 Reflector 来分析一下。
(如果不了解 Reflector 的可用 google 搜索).
WebControl 类的部分代码:
Button 类的部分代码:
TextBox 类部分代码:
Label 类部分代码:
注意其中 // here 标注的地方。这些就是方法调用跳转的地方。
从以上的跟踪我们可以得到一个结论:
只有需要处理回发(PostBack) 的控件才必须放在 Form 内。
这种控件通常要实现 IPostBackEventHandler 或者 IPostBackDataHandler 接口。
而上面代码里的 Page.VerifyRenderingInServerForm(this) 方法正是我们做的试验里异常的抛出者。这个方法检测当前控件是否在一个 runat="server" 的 form 内部,并且在 Render 调用中。如果不是,则抛出异常。
我们看到上面的 Label 类中就保留了基类对 AddAttributesToRender 的默认实现,而没有加入对 Page.VerifyRenderingInServerForm(this) 这个检验的调用机制。
对控件开发者而言,如果你的控件需要处理回发事件或回发数据,则必须实现 IPostBackEventHandler 或 IPostBackDataHandler 接口。并且为了保险起见,应该重写 AddAttributesToRender 虚拟方法的实现,在其中调用 Page.VerifyRenderingInServerForm(this) 这个检验。
前面试验的错误信息里有堆栈跟踪,也说明了这一点:
声明:本例不具有技术含量,仅示例如何利用 Reflector 来弄清楚一些事情而已。
先让我们来做几个试验。
(为了说明方便,以下仅列出 aspx 文件中 body 里的内容。)
1,
<form id="Form1" method="post" runat="server">
</form>
<asp:TextBox ID="txtHello" Runat="server" Text="OK"></asp:TextBox>
</form>
<asp:TextBox ID="txtHello" Runat="server" Text="OK"></asp:TextBox>
2,
<form id="Form1" method="post" runat="server">
</form>
<asp:Label ID="lblHello" Runat="server" Text="Hello"></asp:Label>
</form>
<asp:Label ID="lblHello" Runat="server" Text="Hello"></asp:Label>
3,
<form id="Form1" method="post" runat="server">
</form>
<asp:Button ID="btnHello" Runat="server" Text="Hello"></asp:Button>
</form>
<asp:Button ID="btnHello" Runat="server" Text="Hello"></asp:Button>
结果发现了什么?
Label 可以正常使用。而 TextBox 和 Button 都会出现抛出一个异常。
因为 Label, Button, TextBox 都继承自 System.Web.UI.WebControls.WebControl 类。下面我们用 Reflector 来分析一下。
(如果不了解 Reflector 的可用 google 搜索).
WebControl 类的部分代码:
// (Attributes 省略)
public class WebControl : Control, IAttributeAccessor
{
//
protected override void Render(HtmlTextWriter writer)
{
this.RenderBeginTag(writer); // here
this.RenderContents(writer);
this.RenderEndTag(writer);
}
public virtual void RenderBeginTag(HtmlTextWriter writer)
{
this.AddAttributesToRender(writer); // here
HtmlTextWriterTag tag1 = this.TagKey;
if (tag1 != HtmlTextWriterTag.Unknown)
{
writer.RenderBeginTag(tag1);
}
else
{
writer.RenderBeginTag(this.TagName);
}
}
}
public class WebControl : Control, IAttributeAccessor
{
//
protected override void Render(HtmlTextWriter writer)
{
this.RenderBeginTag(writer); // here
this.RenderContents(writer);
this.RenderEndTag(writer);
}
public virtual void RenderBeginTag(HtmlTextWriter writer)
{
this.AddAttributesToRender(writer); // here
HtmlTextWriterTag tag1 = this.TagKey;
if (tag1 != HtmlTextWriterTag.Unknown)
{
writer.RenderBeginTag(tag1);
}
else
{
writer.RenderBeginTag(this.TagName);
}
}
}
Button 类的部分代码:
// (Attributes 省略)
public class Button : WebControl, IPostBackEventHandler
{
//
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
if (this.Page != null)
{
this.Page.VerifyRenderingInServerForm(this); // here
}
// .
base.AddAttributesToRender(writer);
}
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
if (this.CausesValidation)
{
this.Page.Validate();
}
this.OnClick(new EventArgs());
this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
}
}
public class Button : WebControl, IPostBackEventHandler
{
//
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
if (this.Page != null)
{
this.Page.VerifyRenderingInServerForm(this); // here
}
// .
base.AddAttributesToRender(writer);
}
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
if (this.CausesValidation)
{
this.Page.Validate();
}
this.OnClick(new EventArgs());
this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
}
}
TextBox 类部分代码:
// (Attributes 省略)
public class TextBox : WebControl, IPostBackDataHandler
{
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
int num1;
if (this.Page != null)
{
this.Page.VerifyRenderingInServerForm(this); // here
}
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
TextBoxMode mode1 = this.TextMode;
//
}
}
public class TextBox : WebControl, IPostBackDataHandler
{
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
int num1;
if (this.Page != null)
{
this.Page.VerifyRenderingInServerForm(this); // here
}
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
TextBoxMode mode1 = this.TextMode;
//
}
}
Label 类部分代码:
// (Attributes 省略)
public class Label : WebControl
{
//
}
public class Label : WebControl
{
//
}
注意其中 // here 标注的地方。这些就是方法调用跳转的地方。
从以上的跟踪我们可以得到一个结论:
只有需要处理回发(PostBack) 的控件才必须放在 Form 内。
这种控件通常要实现 IPostBackEventHandler 或者 IPostBackDataHandler 接口。
而上面代码里的 Page.VerifyRenderingInServerForm(this) 方法正是我们做的试验里异常的抛出者。这个方法检测当前控件是否在一个 runat="server" 的 form 内部,并且在 Render 调用中。如果不是,则抛出异常。
我们看到上面的 Label 类中就保留了基类对 AddAttributesToRender 的默认实现,而没有加入对 Page.VerifyRenderingInServerForm(this) 这个检验的调用机制。
对控件开发者而言,如果你的控件需要处理回发事件或回发数据,则必须实现 IPostBackEventHandler 或 IPostBackDataHandler 接口。并且为了保险起见,应该重写 AddAttributesToRender 虚拟方法的实现,在其中调用 Page.VerifyRenderingInServerForm(this) 这个检验。
前面试验的错误信息里有堆栈跟踪,也说明了这一点:
异常详细信息: System.Web.HttpException: 类型“TextBox”的控件“txtHello”必须放在具有 runat=server 的窗体标记内。
堆栈跟踪:
[HttpException (0x80004005): 类型“TextBox”的控件“txtHello”必须放在具有 runat=server 的窗体标记内。]
System.Web.UI.Page.VerifyRenderingInServerForm(Control control) +152
System.Web.UI.WebControls.TextBox.AddAttributesToRender(HtmlTextWriter writer) +40
System.Web.UI.WebControls.WebControl.RenderBeginTag(HtmlTextWriter writer) +17
System.Web.UI.WebControls.TextBox.Render(HtmlTextWriter writer) +17
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +241
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +72
System.Web.UI.Control.Render(HtmlTextWriter writer) +7
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +241
System.Web.UI.Page.ProcessRequestMain() +1900
堆栈跟踪:
[HttpException (0x80004005): 类型“TextBox”的控件“txtHello”必须放在具有 runat=server 的窗体标记内。]
System.Web.UI.Page.VerifyRenderingInServerForm(Control control) +152
System.Web.UI.WebControls.TextBox.AddAttributesToRender(HtmlTextWriter writer) +40
System.Web.UI.WebControls.WebControl.RenderBeginTag(HtmlTextWriter writer) +17
System.Web.UI.WebControls.TextBox.Render(HtmlTextWriter writer) +17
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +241
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +72
System.Web.UI.Control.Render(HtmlTextWriter writer) +7
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +241
System.Web.UI.Page.ProcessRequestMain() +1900
声明:本例不具有技术含量,仅示例如何利用 Reflector 来弄清楚一些事情而已。
posted on 2005-03-22 16:42 NeilChen 阅读(5059) 评论(12) 编辑 收藏 举报