自定义服务器控件(入门)
2015-01-15 09:34
134 查看
转自:http://www.cnblogs.com/SkySoot/archive/2012/11/22/2782916.html
每一种自定义控件都有它的优缺点。以前曾介绍过用户控件。用户控件比自定义服务器控件更容易创建,但服务器控件的功能更强大。
服务器控件在两个方面比用户控件强得多:
服务器控件允许完全控制所生成的HTML
服务器控件提供更好的设计时支持
所有的ASP.NETWeb控件都是服务器控件。现在,你将学会如何创建自己的服务器控件。
很多控件不能直接从Control类派生出来,它们是从System.Web.UI.WebControls.WebControl类派生出来的。这个类添加了一些功能来帮助你实现标准的样式,包括诸如Font、ForeColor、BackColor之类的属性。
为了更好的理解自定义控件是如何工作的,下面会给出几个简单的自定义控件的例子。
生成HTML代码最简单的方法就是使用HtmlTextWriter.Write()方法向页面写一个原始的HTML字符串。显然,不能用Write()方法输出ASP.NET标签或其他服务器端内容,因为在把最终页面发送到客户端之前要为它呈现内容。
[/code]
[/code]
HtmlTextWriter类不仅让你写原始的HTML,还提供了一些很有帮助的方法来帮你管理样式特性和标签。下一个例子给出了同一个控件,但略有一些差别。首先它使用RenderBeginTag()和RenderEndTag()将锚标签的开始和结束标签的呈现分开;其次,它增加了配置这个控件如何显示的样式特性:
[/code]
[/code]
你应该注意到在这个例子里的一些要点。首先,为了简化,例子中使用了几个枚举,这些枚举有助于避免因为排版的细微错误而导致的意外问题。
HtmlTextWriterTag:这个枚举定义了许多HTML标签,如<a>、<p>、<font>等。
HtmlTextWriterAttribute:这个枚举定义了许多公共的HTML标签特性,如onClick、href、align、alt等。
HtmlTextWriterStyle:这个枚举定义了14个样式特性,包括BackgroundColor、BackgroundImage、BorderColor、BorderStyle、BorderWidth、Color、FontFamily、FontSize、FontStyle、FontWeight、Height、Width。所有这些零散的信息以分号分割的形式连接,形成CSS样式信息列表,用来呈现标签的样式特性。
当Render()执行的时候,它首先定义所有将要添加到后续标签上的特性;然后创建开始标签;所有这些特性都被置于这个标签内部。最终呈现的标签如下:
<ahref=”http://www.apress.com”style=”font-size:20;color:Blue;”>ClicktovisitApress</a>
每一种自定义控件都有它的优缺点。以前曾介绍过用户控件。用户控件比自定义服务器控件更容易创建,但服务器控件的功能更强大。
服务器控件在两个方面比用户控件强得多:
服务器控件允许完全控制所生成的HTML
服务器控件提供更好的设计时支持
所有的ASP.NETWeb控件都是服务器控件。现在,你将学会如何创建自己的服务器控件。
自定义服务器控件入门
服务器控件是指那些直接或间接从System.Web.UI.Control类派生出来的.NET类。Control类提供对所有的服务器控件都通用的属性和方法(例如ID,ViewState和Controls集合等)。很多控件不能直接从Control类派生出来,它们是从System.Web.UI.WebControls.WebControl类派生出来的。这个类添加了一些功能来帮助你实现标准的样式,包括诸如Font、ForeColor、BackColor之类的属性。
为了更好的理解自定义控件是如何工作的,下面会给出几个简单的自定义控件的例子。
创建简单的自定义控件
为了创建一个基本的自定义控件,你的类需要从Control类派生并覆盖Render()方法。Render()接收一个HtmlTextWriter对象,用来为控件生成HTML。生成HTML代码最简单的方法就是使用HtmlTextWriter.Write()方法向页面写一个原始的HTML字符串。显然,不能用Write()方法输出ASP.NET标签或其他服务器端内容,因为在把最终页面发送到客户端之前要为它呈现内容。
[code] [code]publicclassLinkControl:Control
{
//继承Control类,重写Render方法输出一个A标签
protectedoverridevoidRender(HtmlTextWriterwriter)
{
writer.Write("<ahref='http://www.apress.com'>ClicktovisitApress</a>");
}
}
[/code]
[/code]
HtmlTextWriter类不仅让你写原始的HTML,还提供了一些很有帮助的方法来帮你管理样式特性和标签。下一个例子给出了同一个控件,但略有一些差别。首先它使用RenderBeginTag()和RenderEndTag()将锚标签的开始和结束标签的呈现分开;其次,它增加了配置这个控件如何显示的样式特性:
[code] [code]publicclassLinkControl:Control
{
protectedoverridevoidRender(HtmlTextWriterwriter)
{
writer.AddAttribute(HtmlTextWriterAttribute.Href,"http://www.apress.com");
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"20");
writer.AddStyleAttribute(HtmlTextWriterStyle.Color,"Blue");
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.Write("ClicktovisitApress");
writer.RenderEndTag();
}
}
[/code]
[/code]
你应该注意到在这个例子里的一些要点。首先,为了简化,例子中使用了几个枚举,这些枚举有助于避免因为排版的细微错误而导致的意外问题。
HtmlTextWriterTag:这个枚举定义了许多HTML标签,如<a>、<p>、<font>等。
HtmlTextWriterAttribute:这个枚举定义了许多公共的HTML标签特性,如onClick、href、align、alt等。
HtmlTextWriterStyle:这个枚举定义了14个样式特性,包括BackgroundColor、BackgroundImage、BorderColor、BorderStyle、BorderWidth、Color、FontFamily、FontSize、FontStyle、FontWeight、Height、Width。所有这些零散的信息以分号分割的形式连接,形成CSS样式信息列表,用来呈现标签的样式特性。
当Render()执行的时候,它首先定义所有将要添加到后续标签上的特性;然后创建开始标签;所有这些特性都被置于这个标签内部。最终呈现的标签如下:
<ahref=”http://www.apress.com”style=”font-size:20;color:Blue;”>ClicktovisitApress</a>
HtmlTextWriter类的关键方法
AddAttribute() | 添加任意的HTML特性及其值到一个HtmlTextWriter的输出流。这个特性自动被应用到通过调用RenderBeginTag()创建的下一个标签。(有枚举值可选) |
AddStyleAttribute | 添加任意的HTML样式特性及其值到一个HtmlTextWriter的输出流。这个特性自动被应用到通过调用RenderBeginTag()创建的下一个标签。(有枚举值可选) |
RenderBeginTag() | 输出HTML元素的开始标签。(有枚举值可选) |
RenderEndTag() | 输出当前HTML元素的结束标签,不必指定标签名称 |
WriteBeginTag() | 与RenderBeginTag()相似,但不输出开始标签的结束字符>。这意味着可以调用WriteAttribute()给标签添加更多的特性。要关闭开始标签,可以调用Write(HtmlTextWriter.TagRightChar) |
WriteAttribute() | 输出一个特性到输出流,必须跟在WriteBeginTag()之后调用 |
WriteEndTag() | 给当前的HTML元素输出一个元素的结束符号 |
使用自定义控件
要使用自定义控件,你需要让它在Web应用程序中可用。有两种方法:把源代码复制到App_Code目录下
把编译后的程序集添加到Bin目录下(添加引用)
对于那些能使用自定义控件的页面,必须使用Register指令(相似于用户控件),不仅必须包括一个TagPrefix标签,还要指定程序集文件,以及控件类所在的命名空间。你不需要指定TagName,因为服务器控件的类名自动被使用。
<%@RegisterTagPrefix="apress"Assembly="UserDesignControl"Namespace="UserDesignControl"%>
如果控件位于当前Web程序的App_Code目录下,就不需要Assembly特性。
<%@RegisterTagPrefix="apress"Namespace="UserDesignControl"%>
你可以重用标签前缀,换言之,把两个不同的命名空间或者两个完全不同的程序集映射到同一个标签前缀完全有效。
如果想要在同一个Web应用程序的多个页面上使用同一个控件,可以用如下的方式配置web.config文件:
[code] [code]<configuration>
<system.web>
<pages>
<controls>
<addtagPrefix="apress"namespace="UserDesignControl"assembly="UserDesignControl"/>
</controls>
</pages>
...
</system.web>
</configuration>
[/code]
[/code]
当你从VS工具箱中拖曳一个控件的时候,VS会选择一个默认的前缀。但配置了web.config后,就可以将你的控件前缀标准化。
工具箱中的自定义控件
为了便于使用,你也许会想要让这些自定义控件出现在工具箱里。如果你在单独的程序集里创建自定义控件的话,VS的工具箱内置了对自定义控件的支持。一旦创建了项目,就能定义控件了。开发控件库项目的方式与开发其他DLL组件一样。可以随时构建项目,但是不能直接启动它,因为它不是一个实际的应用程序。为了测试控件,需要在另一个应用程序中使用它们。
VS每次编译项目时,被引用的程序集的最新版本将被复制到Web应用程序的Bin目录下,也就是说,不用担心更新过被引用的控件版本。
自动工具箱(在设计选项卡时)会自动侦测Web程序中包含的自定义控件,并将它添加到一个临时的项目专用的区域:
如果你想要控件在任何Web程序中都可用,但又不想让Web应用程序开发人员更改你的自定义控件的代码,在这种情况下,你只需部署编译后的程序集。然后,你可以永久性的添加这些控件到工具箱中(选项卡查找程序集)。
创建支持样式属性的Web控件
前一个自定义的例子不允许网页定制控件的外观。LinkControl控件没有提供任何属性来设置所生成的HTML标签的前景色、背景色、字体及其他特性。换句话说,LinkControl控件不允许外部代码改变它生成的HTML。为了更灵活,需要显式添加代表这些属性值的公共属性,然后需要在Render()中读取这些属性并生成相应的HTML代码。样式属性是许多HTML控件用到的基础设施的基础部分。比较理想的是,所有的控件应该遵循同一个有效的样式信息的简化模型,并且不强迫自定义控件的开发者自己编写这个通用的功能。ASP.NET通过WebControl基类实现了这点(System。Web.UI.Controls命名空间中)。ASP.NET中的每个控件都从WebControl类派生出来,因此,你也可以从WebControl类中派生出自定义控件。
WebControl类不仅包含基本的样式相关属性,如Font、ForeColor、BackColor等,它还自动的把这些属性呈现在控件标签里。这里给出其工作原理:WebControl假设它应该添加这些特性到一个HTML标签,称作基本标签(basetag);如果你在输出多个元素,这些特性被添加到包含其他元素的最外围元素。你应在构造函数里为Web控件指定基本标签。
最终,你不用覆盖Render()方法,WebControl类已经包含了Render()的实现,这个实现由以下3个方法构成:
RenderBeginTag():输出控件的开始标签以及你指定的特性
RenderContents():输出开始标签和结束标签中间的所有内容,包括文本内容和其他HTML标签。这个方法是经常覆盖来输出自定义控件内容的方法
RenderEndTag():输出控件的结束标签
当然,如果需要也可以通过覆盖Render()方法来改变这一行为。但是,如果这个基本框架适合你的需要,你只需写很少的自定义代码就能实现所需功能。
下一例子显示了从WebControl派生出的一个新的链接控件,由此获得了样式属性的自动支持:
[code]
[code]publicclassLinkWebControl:WebControl
{
//WebControl有多个版本的构造函数,其中有允许你传入标签的构造函数
//这里传递的基本控件标签是锚标记<a>
publicLinkWebControl()
:base(HtmlTextWriterTag.A)
{
//构造函数不需要任何的代码.
//唯一重要的是,利用这个机会调用WebControl构造函数来设置基本控件标签
}
//定义两个属性,允许网页设置文本和URL
publicstringText{get;set;}
privatestringhyperLink;
publicstringHyperLink
{
get{returnhyperLink;}
set
{
if(value.IndexOf("http://")==-1)
{
thrownewApplicationException("SpecifyHTTPastheprotocol");
}
else
{
hyperLink=value;
}
}
}
//在定义文本和超链接变量的时候,你可以将其设置为空字符串
//这里故意覆盖了OnInit()方法来显示怎么通过编程初始化一个控件
protectedoverridevoidOnInit(EventArgse)
{
base.OnInit(e);
//ifnovalueweresetinthecontroltag,applythedefaultsnow.
if(hyperLink==null)
hyperLink="http://www.google.com";
if(Text==null)
Text="Clicktosearch";
}
//WebControl类可以覆盖下列方法,因此能添加额外标签
protectedoverridevoidAddAttributesToRender(HtmlTextWriterwriter)
{
writer.AddAttribute(HtmlTextWriterAttribute.Href,this.HyperLink);
base.AddAttributesToRender(writer);
}
//添加文本
protectedoverridevoidRenderContents(HtmlTextWriterwriter)
{
writer.Write(this.Text);
base.RenderContents(writer);
}
}
[/code]
[/code]
注意,无论何时自定义控件覆盖一个方法,它都应该通过base调用基类的实现。这样做确保你不会意外的抑制住其他需要运行的代码。通常,基类方法所做的就是激发一个相关事件。例如,如果你覆盖了RenderBeginTag()方法,而且又没有调用基类实现,呈现代码将会失败并抛出一个未处理的异常,因为这个标签不是打开的。
VisualStudio中的自定义服务器控件
手工创建的控件和VisualStudio生成的控件有一个重要的区别。由VS生成的控件包含了一些自动生成的样板代码:文件的开始有一组using语句,它们导入有用的ASP.NET命名空间
控件类添加了Text属性,它被保存到视图状态里
控件类覆盖了RenderContents()以输出Text属性的内容
控件类声明和Text属性被配置了设计时支持的特性修饰(如DefaultProperty表示选中控件时,突出显示的属性)
没有VS的帮助,增加这些细节也非常容易。因此不必害怕空代码文件以及手工编写自定义控件类。
呈现过程
前面的例子介绍了几个新的呈现方法,现在让我们看看它们是如何协同工作的。呈现过程的起点是RenderControl()方法。RenderControl()是ASP.NET将每个控件呈现成HTML的公共呈现方法,你不能覆盖它。RenderControl()调用启动呈现过程的Render()方法,你可以覆盖这个方法,如第一个例子所示。然而,如果你覆盖了Render()方法,而没有调用Render()方法的基类实现,其他的呈现方法都不会被激发。
Render()方法的基类实现调用了RenderBeginTag()、RenderContents()、RenderEndTag()方法,如前例所示。这里还有一个小插曲,RenderContents()方法的基类实现调用了另一个呈现方法RenderChildren()。这个方法遍历了Controls集合中的子控件集合,并调用了每个子控件的RenderControl()方法。通过这种方式,你可以很容易的基于其他控件构建自己的控件。在后续的组合控件文章中我会介绍。
那么,应该覆盖哪一个呈现方法呢?
如果想用新的内容替换整个呈现过程,或者想在基本控件标签之前添加HTML内容(如Javascript代码块),你可以覆盖Render()方法。
如果想利用自动的样式特性,你应该定义一个基本标签(调用基类构造函数并指定标签名参数),并覆盖RenderContents()。
如果想阻止子控件被显示或者要定制它们的呈现方式(例如,让她们以相反的顺序呈现),你可以覆盖RenderChildren()。
值得注意的是,你可以调用RenderControl()方法来检查一个控件的HTML输出,这个技术是一个调试时的便捷方式:
[code]
[code]StringWriterwriter=newStringWriter();
HtmlTextWriteroutput=newHtmlTextWriter(writer);
LinkWebControl1.RenderControl(output);
Label1.InnerHtml="TheHTMLforLinkWebControl1is<br/><blockquote>"
+Server.HtmlEncode(writer.ToString())+"<blockquote/>";
[/code]
[/code]
这个方法不仅仅是用于调试的,也可以用来简化你的呈现代码。例如,你也许发现创建和配置一个HtmlTable控件,然后调用它的RenderControl()方法是比较容易的,而不是直接输出<table>、<tr>、<td>标签到输出流。