UserControl 用户自定义控件

 

关于用户自定义控件,想必大家已经非常熟悉了。虽然说经常用过,但是只是简单的使用而已。在这里再次总结一下Asp.net中的UserControl,以便下次使用时

能够得心应手。本文将会介绍以下内容:


1,什么是UserControl?
2,如何定义一个UserControl?
3,如何使用UserControl?
4,如何通过UserControl属性来控制html?
5,如何实现<u1:Control>string</u1:Control>?

 

1,什么是UserControl?

 

关于UserControl的解释MSDN,跟Wikipedia都有介绍:

http://msdn.microsoft.com/en-us/library/system.web.ui.usercontrol.aspx
http://en.wikipedia.org/wiki/ASP.NET#User_controls

说白了,UserControl的存在就是为了重用html代码。有点类似php的include或者require,但是它比include或require更加灵活,它不当只是
单纯的引入代码,而且通过设置UserControl的属性来对html代码进行控制,从而更好的实现代码复用。基本UserControl的使用方法更aspx页面
是一样的,但是UserControl不可以通过url来访问,只能在页面或者其它用户控件中访问。

 

2,如何定义一个简单UserControl?

 
新建UserControl方法:右键asp.net web项目->添加->添加新项->Web->Web用户控件。打开控件的后台代码,我们可以看到,控件
继承于System.Web.UI.UserControl类。新建好的控件除了后缀名更asp.net不同之外,其它结构都一样,用法也基本一致。
这是,你就可以在ascx文件添加html代码了。

 

3,如何使用UserControl?

 


在页面中使用UserControl只需要在页面的头部添加Register代码段:
<%@ Register src="UserControl/UC_Demo.ascx" tagname="Demo" tagprefix="uc1" %>
src表示用户控件所在的相对路径。
添加完上述代码段之后,就可以你需要使用的地方使用了。
使用方法如下,我们可以看到uc1就是上面定义的tagprefix,Demo就是上面定义的tagname,由于用户控件也是一种服务端控件
因此我们这里必须加上runat="server",否则.net会认为是html标签。
<uc1:Demo ID="aaa" runat="server">
</uc1:Demo>

 

4,如何自定义属性来控制UserControl的html?

 
第2,3部分已经介绍了如何新建以及使用一个简单的用户控件。但是,上面的例子只能满足对于简单html的复用,比如说一些
头部信息或底部信息。但对于一些复杂的模块,显然我们需要一些属性来控制它。比如说:我们定义一个用户控件,
该控件需要用户可以自定义主题。即用户希望能够这样的使用:
<uc1:Demo ID="aaa" ThemeName="green" runat="server">
</uc1:Demo>

那么该怎么实现呢?首先,我们的第一印象就是,Theme应该是一个enum类型,可供用户选择,因此,我们首先定义一个Theme的枚举
   

public enum Theme
{
     Brown = 10,
     Cyan = 20,
     Gray = 30,
     Green = 40,
      Leaf = 50,
      Plain = 60,
      Purple = 70
}

    
 OK,枚举有了,那么我们应该如何将该枚举变成UserControl中可设置的选项呢?在UserControl的后台代码中(即ascx.cs文件),
 我们只需为该控件类定义一个public的共有变量即可。因此,我们只要在后台类文件中,添加如下代码就可以实现上述需要:
 public Theme ThemeName;
 我们可以通过私有成员来设置初始值,这时候代码就变成这样:

private Theme _ThemeName = Theme.Cyan;

public Theme ThemeName
{
     get { return this._ThemeName; }
     set { _ThemeName = value; }
}

此时,用户可以通过属性来设置Theme了,Theme在页面中设置后,用户控件就可以通过Theme来控制它的html代码中的样式了。

 

5,如何实现<u1:Control>string</u1:Control>?

 
这个是本文中要讲的重点。在使用用户控件的过程中,有时候是一个模块(如下图),我只需要重用这个框,而这个框里的内容
是要自定义的。那么我们要怎么办呢?首先我们想到的是属性,但是我们不可能将一大串的html代码作为属性传递过去,
这样代码就太恶心了。因此我们就想到了标题的那种方式。将html夹在控件中间。还是接着上面所说的例子,这时,我们希望
用户可以这样调用:

<uc1:Demo ID="aaa" ThemeName="green" runat="server">
 <div style="width:100px;height:100px;backgroud:red;">
  hello world!
 </div>
</uc1:Demo>

 

通过google,我找到了解决办法。asp.net提供了ITextControl接口,通过该接口我们就可以实现上述的功能。因此,我们需要做的
就是:第一步,让UserControl实现ITextControl接口。第二步,实现ITextControl的Text字段。第三步,重写Control类中的AddParsedSubObject方法。

假设我们有好几个这样的控件,因此,我们将以上三步实现的内容抽象到一个类中,我们暂且叫做DemoWidget,代码如下:

 

  [ParseChildren(false)]
    public class DemoWidget : System.Web.UI.UserControl, ITextControl
    {
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public virtual string Text
        {
            get
            {
                object obj2 = this.ViewState["Text"];
                if (obj2 != null)
                {
                    return (string)obj2;
                }
                return string.Empty;
            }
            set
            {
                if (this.HasControls())
                {
                    this.Controls.Clear();
                }
                this.ViewState["Text"] = value;
            }
        }

        protected override void AddParsedSubObject(object obj)
        {
            if (obj is LiteralControl)
            {
                HtmlContent.Append(((LiteralControl)obj).Text);
                this.Text = HtmlContent.ToString();
            }
            else
            {
                if (obj != null)
                {
                    HtmlContent.Append(GetControlHtml(obj as Control));
                    this.Text = HtmlContent.ToString();
                }
            }
        }
        protected StringBuilder HtmlContent = new StringBuilder();
        protected string GetControlHtml(Control ctl)
        {
            StringBuilder sb = new StringBuilder();
            StringWriter tw = new StringWriter();
            HtmlTextWriter writer = new HtmlTextWriter(tw);
            ctl.RenderControl(writer);
            sb.Append(writer.InnerWriter.ToString());
            return sb.ToString();
            //base.AddParsedSubObject(new LiteralControl(tmpStr));
            //this.Text = tmpStr;
        }
    }


我们来看一下,DemoWidget是如何实现上述的三个步骤的。很明显第一步已经完成。

 

第二步我们可以看到也实现了ITextControl的Text字段,Text字段就是用于保存夹在用户控件中间的文本,从代码中我们可以看到,我将这些文本信息保存到viewstate中。
而Text上方的属性[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
又表示什么呢?msdn的解释如下:
InnerDefaultProperty 指定属性在 ASP.NET 服务器控件中保持为内部文本。还指示将该属性定义为元素的默认属性。只能指定一个属性为默认属性。
http://msdn.microsoft.com/zh-cn/library/system.web.ui.persistencemode.aspx
也就是说,Text将作为DemoWidget的默认属性使用,而且只能有一个这样的属性。

 

第三步,重写AddParsedSubObject方法
为什么要重新AddParsedSubObject方法呢?我们首先添加一个空的AddParsedSubObject方法,设置一个断点,然后调试,我们可以发现,当DemoWidget中间有文本时,
都会调用该函数,因此,这里我们可以将html赋值给Text。默认,传递进该函数的对象是LiteralControl类型的。因此我们实现如下代码:
该方法的具体解释请查看msdn:http://msdn.microsoft.com/zh-cn/library/system.web.ui.control.addparsedsubobject.aspx
 protected override void AddParsedSubObject(object obj)
 {
   if (obj is LiteralControl)
    {
         this.Text = ((LiteralControl)obj).Text;
    }
 }
 
 这里还需要注意的是:我们必须为DemoWidget类添加属性[ParseChildren(false)],ParseChildren属性表示是否将服务器控件标记内的元素解释为属性,因此,这里应该为false。
 http://msdn.microsoft.com/zh-cn/library/system.web.ui.parsechildrenattribute.aspx
 
 好了,之前提到的三步我们都已经完成了,可是我发现类还有其它代码,其它代码又是干嘛的呢?此时,我们发现如果刚刚三步虽实现了一开始提出的需求,
 但是,上述类还有一定的局限性,就是控件标记内只能包含html文本,当控件标记内需要包含另外一个控件的时候就有问题了。

 <uc1:Demo ID="aaa" ThemeName="green" runat="server">
 <div style="width:100px;height:100px;backgroud:red;">
  hello world!
 </div>
 <uc1:Demo ID="bbb" ThemeName="Leaf" runat="server">
  <div style="width:100px;height:100px;backgroud:red;">
   hello world!
  </div>
 </uc1:Demo>
</uc1:Demo>

由于控件标记中包含了其它控件,因此,AddParsedSubObject中的参数obj就有可能是子控件类型,因此我们必须修改AddParsedSubObject函数,并新建HtmlContent成员,用于保存子控件的html代码
然后再将该子控件生成的html代码赋值给控件的Text属性,GetControlHtml方法就是用于获取子控件生成的html代码。而AddParsedSubObject则变成如下所示:

  

  protected override void AddParsedSubObject(object obj)
        {
            if (obj is LiteralControl)
            {
                HtmlContent.Append(((LiteralControl)obj).Text);
                this.Text = HtmlContent.ToString();
            }
            else
            {
                if (obj != null)
                {
                    HtmlContent.Append(GetControlHtml(obj as Control));
                    this.Text = HtmlContent.ToString();
                }
            }
        }

       
这时,你就可以随意在UserControl标签里添加任何东西了,你可以添加html代码,也可以添加自定义控件,甚至还可以添加asp.net服务端控件。

嗯,关于UserControl的内容就介绍到这里,有什么讲得不对的地方请大家指正,同时也期望得到大家的鼓励。

 

这里是源代码

posted @ 2010-09-07 12:06  Chris Cheung  阅读(21312)  评论(3编辑  收藏  举报