开发此简单的自定义标签控件之前,我们先来看看要注意哪几点:
1.需要一个资源文件或xml文件,用来存储相关数据,如下图所示:

其相关的代码如下:(可在.net中新建一个.resx文件)
<?xml version="1.0" encoding="utf-8" ?>2
<root>3
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">4
<xsd:element name="root" msdata:IsDataSet="true">5
<xsd:complexType>6
<xsd:choice maxOccurs="unbounded">7
<xsd:element name="data">8
<xsd:complexType>9
<xsd:sequence>10
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />11
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />12
</xsd:sequence>13
<xsd:attribute name="name" type="xsd:string" />14
<xsd:attribute name="type" type="xsd:string" />15
<xsd:attribute name="mimetype" type="xsd:string" />16
</xsd:complexType>17
</xsd:element>18
<xsd:element name="resheader">19
<xsd:complexType>20
<xsd:sequence>21
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />22
</xsd:sequence>23
<xsd:attribute name="name" type="xsd:string" use="required" />24
</xsd:complexType>25
</xsd:element>26
</xsd:choice>27
</xsd:complexType>28
</xsd:element>29
</xsd:schema>30
<resheader name="ResMimeType">31
<value>text/microsoft-resx</value>32
</resheader>33
<resheader name="Version">34
<value>1.0.0.0</value>35
</resheader>36
<resheader name="Reader">37
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>38
</resheader>39
<resheader name="Writer">40
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>41
</resheader>42
<data name="Add">43
<value>增加</value>44
</data>45
<data name="Delete">46
<value>删除</value>47
</data>48
<data name="UserName">49
<value>用户名</value>50
</data>51
<data name="Pwd">52
<value>密码</value>53
</data>54
</root>Control类:这是所有标准服务器控件的基类。该类提供了三个方法用来控件呈现,看下面的片段代码:
//RenderControl方法的基本实现2
public void RenderControl(HtmlTextWriter writer)3
{4
if(Visible)5
{6
Render(writer);7
}8
}9
//Render方法基本实现10
protected virtual void Render(HtmlTextWriter writer)11
{12
RenderChildren(writer);13
}14
//RenderChildren方式基本实现15
protected virtual void RenderChildren(HtmlTextWriter writer)16
{17
foreach (Control c in Controls)18
{19
c.RenderControl(writer);20
}21
}22

(1)RenderControl方法
先判断其Visible然后调用Render方法
(2) Render方法
使用TextWriter将标记字符和文本输出然后调用RenderChildren方法
(3)RenderChildren方法
判断当前控件是否有子控件,然后再调用RenderControl方法根据子控件的Visible值输出子控件.
注意:
如果我们重写了RenderControl,则享受不到Visible属性给我们带来的便利,所以如果我们继承Control类,只能重写Render方法。
准备工作完成了,我们来看以下代码:
using System;2
using System.Resources;3
using System.Collections;4
using System.Reflection;5
using System.Text;6
using System.Web.UI;7
using System.Web.UI.WebControls;8
using System.ComponentModel;9

10

11
namespace Component12
{13
/// <summary>14
/// myResources 的摘要说明。15
/// </summary>16
/// [DefaultProperty("Text"), 17
[DefaultProperty("Text"), 18
ToolboxData("<{0}:myResources runat=server></{0}:myResources>")]19
public class myResources:Control20
{21
Assembly assembly;22
ResourceManager rmManager;23
private string text;24
string s="";25
private const string Defalut="默认值";26
[Bindable(true), 27
Category("Appearance"), 28
DefaultValue("")] 29
public string Text30
{31
get32
{33
return text;34
}35
set36
{37
text=value;38
}39
}40
41
public myResources()42
{ 43
//44
// TODO: 在此处添加构造函数逻辑45
//46
}47
protected override void Render(HtmlTextWriter output)48
{49
assembly = Assembly.GetExecutingAssembly();50
rmManager = new ResourceManager("Component.cn",assembly);51
52
if(Text==null)53
{54
output.Write(Defalut);55
}56
else57
{58

59
s = rmManager.GetString(Text);60
if(s==null)61
{62
output.Write(Defalut);63
}64
else65
{66
output.Write(s);67
}68
}69
base.Render(output);70
}71
}72
}73

以上代码实现了当当在Label的Text中输入Add时,界面会显示增加,如果要换成其它版本的,直接修改.resx文件即可。
如果需要在项目中选择日文版本,可根据需要动态加载不同的.resx文件。(读者自行在项目中扩展)。
4.现在当我们运行以上事例,希望能够调整服务器控件都具有的公共样式时,发现并没有类似于以下图的样式:
如果我们此时改变继承的基类,将Control类改为WebControl类,呈现控件时不用Render了,改用RenderContents,则可享受一般服务器控件都享有的公用样式。代码如下:
using System;2
using System.Resources;3
using System.Collections;4
using System.Reflection;5
using System.Text;6
using System.Web.UI;7
using System.Web.UI.WebControls;8
using System.ComponentModel;9

10

11
namespace Component12
{13
/// <summary>14
/// myResources 的摘要说明。15
/// </summary>16
/// [DefaultProperty("Text"), 17
[DefaultProperty("Text"), 18
ToolboxData("<{0}:myResources runat=server></{0}:myResources>")]19
public class myResources:WebControl20
{21
Assembly assembly;22
ResourceManager rmManager;23
private string text;24
string s="";25
private const string Defalut="默认值";26
[Bindable(true), 27
Category("Appearance"), 28
DefaultValue("")] 29
public string Text30
{31
get32
{33
return text;34
}35
set36
{37
text=value;38
}39
}40
41
public myResources()42
{ 43
//44
// TODO: 在此处添加构造函数逻辑45
//46
}47
protected override void RenderContents(HtmlTextWriter output)48
{49
assembly = Assembly.GetExecutingAssembly();50
rmManager = new ResourceManager("Component.cn",assembly);51
52
if(Text==null)53
{54
output.Write(Defalut);55
}56
else57
{58

59
s = rmManager.GetString(Text);60
if(s==null)61
{62
output.Write(Defalut);63
}64
else65
{66
output.Write(s);67
}68
}69
base.Render(output);70
}71
}72
}73

为什么只有继承WebControl类并且一定要重写RenderContents而不是Render方法呢?
看下面WebControl基类中的Render方法的实现代码:
protected override void Render(HtmlTextWriter output)2
{3
RenderBeginTag(output);4
RenderContents(output);5
RenderEndTag(output);6
}接着再看RenderBeginTag方法的定义,如下:
public virtual void RenderBeginTag(HtmlTextWriter output)2
{3
//添加呈现控件的属性和样式4
//AddAttributesToRender为WebControl类中的方法5
AddAttributesToRender(output);6
//呈现控件标签7
//如label控件呈现<span >8
//textbox控件呈现<input >9
HtmlTextWriterTag tagKey = TagKey;10
if (tagKey != HtmlTextWriterTag.Unknown)11
{12
output.RenderBeginTag(tagKey);13
}14
else15
{16
output.RenderBeginTag(this.TagName);17
}18
}如果你还不能理解,你可以这样:将以上多国语言控件修改后测试,你会发现如下几点:
(1).如果继承了WebControl类,重写Render方法时,当在页面中使用时,公用样式无效。
(2).如果继承了WebControl类,重写RenderContents方法时,当在页面中使用时,会自动加上默认的样式<span>....</span>,这样将Label当成了一个整体,此时公用样式就有效了。
(3).如果呈现的是TextBox,会自动加上<input>标签。
现在,当我们希望改变自定义标签的样式时,应该怎么办呢?
通过上面的RenderBeginTag方法,我们可以看到首先调用的是
//添加呈现控件的属性和样式
//AddAttributesToRender为WebControl类中的方法
AddAttributesToRender(output);
在 AddAttributesToRender(output);事件完成后,就进入了HtmlTextWrite的Html tag的步骤,任何绘制属性的动作就不能进行了,因此这些动作在绘制RenderBeginTag之前完成,关于这一点,我们可以验证如下,看以下代码:
protected override void RenderContents(HtmlTextWriter writer)
{
assembly = Assembly.GetExecutingAssembly();
rmManager = new ResourceManager("Component.cn",assembly);
if(Text==null)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(Defalut);
writer.RenderEndTag();
}
else
{
s = rmManager.GetString(Text);
if(s==null)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(Defalut);
writer.RenderEndTag();
}
else
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(s);
writer.RenderEndTag();
}
}
base.RenderContents(writer);
}此时,我们可以看到在RenderBeginTag方法之前,我们定义了标签样式:writer.AddStyleAttribute(...)这样当我们运行此控件后,查看源代码时会发现如下样式:
<span id="MulLanguage1" style="background-color:#ff00ff;Z-INDEX: 101; LEFT: 400px; POSITION: absolute; TOP: 288px">
默认值
</span>
此时我们可以看到样式起作用了。细心的朋友可能会注意了,此控件的标签为<span>...</span>,大家知道,有时我们并不希望用<span>标签,如果我们希望改变这个标签,希望改变此标签为<div>呢,关于为何要用<div>而不用<span>,原因如下:
DIV 和 SPAN 元素最大的特点是默认都没有对元素内的对象进行任何格式化渲染。主要用于应用样式表。两者最明显的区别在于DIV是块元素,而SPAN是行内元素(也译作内嵌元素)。大家可以写个测例看看两者的区别:
(1).所谓块元素,是以另起一行开始渲染的元素,行内元素则不需另起一行,测试一下下面的代码你会有更形象的理解:
测试<span>紧跟前面的"测试"显示</span><div>这里会另起一行显示</div>
(2).块元素和行内元素也不是一成不变的,通过定义CSS的display属性值可以互相转化,如:
测试<div style="display:inline">紧跟前面的"测试"显示</div><span style="display:block">这里会另起一行显示</span>
提示:如果不对DIV元素定义任何CSS属性,其显示效果将行将于P元素。
现在我们只需要改变其构造:如下:
public MulLanguage():base(HtmlTextWriterTag.Div)
{
}此时AddAttributesToRender方法会根据此标签重写其样式,代码如下:
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,"#ff00ff");
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
base.AddAttributesToRender (writer);
}此时,客户端代码会显示如下:
<div id="MulLanguage1" style="background-color:#ff00ff;font-size:12px;Z-INDEX: 101; LEFT: 376px; POSITION: absolute; TOP: 256px">
<span style="font-size:12px;">增加</span>
</div>到现在为止,我们已经弄清楚了以下几个问题:
1.为什么要重写RenderContents而不是Render?
2.何时应重写AddAttributesToRender方法?
3.如何改变自定义控件的标签?
完整代码如下:
using System;
using System.Resources;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace Component
{
/// <summary>
/// MulLanguage 的摘要说明。
/// </summary>
[DefaultProperty("Text"),
ToolboxData("<{0}:MulLanguage runat=server></{0}:MulLanguage>")]
public class MulLanguage : System.Web.UI.WebControls.WebControl
{
Assembly assembly;
ResourceManager rmManager;
private string text;
string s="";
private const string Defalut="默认值";
[Bindable(true),
Category("Appearance"),
DefaultValue("")]
public string Text
{
get
{
return text;
}
set
{
text=value;
}
}
public MulLanguage():base(HtmlTextWriterTag.Div)
{
}
protected override void RenderContents(HtmlTextWriter writer)
{
assembly = Assembly.GetExecutingAssembly();
rmManager = new ResourceManager("Component.cn",assembly);
if(Text==null)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(Defalut);
writer.RenderEndTag();
}
else
{
s = rmManager.GetString(Text);
if(s==null)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(Defalut);
writer.RenderEndTag();
}
else
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(s);
writer.RenderEndTag();
}
}
base.RenderContents(writer);
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor,"#ff00ff");
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize,"12px");
base.AddAttributesToRender (writer);
}
}
}

浙公网安备 33010602011771号