写控件,你准备好了吗(三)?

    上篇讲了一点基础的知识,这次以多国语言标签为例,其思想源于一次外包项目中的,客户要求英语,中文,日文能够随意方便切换。
    开发此简单的自定义标签控件之前,我们先来看看要注意哪几点:
     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方法。

准备工作完成了,我们来看以下代码:
 1 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  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,则可享受一般服务器控件都享有的公用样式。代码如下:

 1 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  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方法的实现代码:

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

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

 1 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 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);
        }

    }

}


















 

你可能感兴趣的:(控件)