上篇讲了一点基础的知识,这次以多国语言标签为例,其思想源于一次外包项目中的,客户要求英语,中文,日文能够随意方便切换。
    开发此简单的自定义标签控件之前,我们先来看看要注意哪几点:
     1.需要一个资源文件或xml文件,用来存储相关数据,如下图所示:

其相关的代码如下:(可在.net中新建一个.resx文件)
 1<?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>
    2.开发此控件继承类时,一般有以下几种类的选择:
       Control类:这是所有标准服务器控件的基类。该类提供了三个方法用来控件呈现,看下面的片段代码:
 1//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方法。

准备工作完成了,我们来看以下代码:
 1using System;
 2using System.Resources;
 3using System.Collections;
 4using System.Reflection;
 5using System.Text;
 6using System.Web.UI;
 7using System.Web.UI.WebControls;
 8using System.ComponentModel;
 9
10
11namespace Component
12{
13    /// <summary>
14    /// myResources 的摘要说明。
15    /// </summary>
16    ///     [DefaultProperty("Text"), 

17    [DefaultProperty("Text"), 
18    ToolboxData("<{0}:myResources runat=server></{0}:myResources>")]
19    public class myResources:Control
20    {
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 Text
30        {
31            get
32            {
33                return text;
34            }

35            set
36            {
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            else
57            {
58
59                s = rmManager.GetString(Text);
60                if(s==null)
61                {
62                    output.Write(Defalut);
63                }

64                else
65                {
66                    output.Write(s);
67                }

68            }

69            base.Render(output);
70        }

71    }

72}

73


    以上代码实现了当当在Label的Text中输入Add时,界面会显示增加,如果要换成其它版本的,直接修改.resx文件即可。
    如果需要在项目中选择日文版本,可根据需要动态加载不同的.resx文件。(读者自行在项目中扩展)。

4.现在当我们运行以上事例,希望能够调整服务器控件都具有的公共样式时,发现并没有类似于以下图的样式:

   如果我们此时改变继承的基类,将Control类改为WebControl类,呈现控件时不用Render了,改用RenderContents,则可享受一般服务器控件都享有的公用样式。代码如下:

 1using System;
 2using System.Resources;
 3using System.Collections;
 4using System.Reflection;
 5using System.Text;
 6using System.Web.UI;
 7using System.Web.UI.WebControls;
 8using System.ComponentModel;
 9
10
11namespace Component
12{
13    /// <summary>
14    /// myResources 的摘要说明。
15    /// </summary>
16    ///     [DefaultProperty("Text"), 

17    [DefaultProperty("Text"), 
18    ToolboxData("<{0}:myResources runat=server></{0}:myResources>")]
19    public class myResources:WebControl
20    {
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 Text
30        {
31            get
32            {
33                return text;
34            }

35            set
36            {
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            else
57            {
58
59                s = rmManager.GetString(Text);
60                if(s==null)
61                {
62                    output.Write(Defalut);
63                }

64                else
65                {
66                    output.Write(s);
67                }

68            }

69            base.Render(output);
70        }

71    }

72}

73

为什么只有继承WebControl类并且一定要重写RenderContents而不是Render方法呢?
看下面WebControl基类中的Render方法的实现代码:

1protected override void Render(HtmlTextWriter output)
2{
3 RenderBeginTag(output);
4 RenderContents(output);
5 RenderEndTag(output);
6}

接着再看RenderBeginTag方法的定义,如下:

 1public 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 else
15 {
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);
        }

    }

}


















 

posted on 2006-09-22 16:17  jasonM  阅读(453)  评论(2编辑  收藏  举报