自定义控件研究

来源:互联网 发布:淘宝限制我发布宝贝 编辑:程序博客网 时间:2024/06/10 05:38

一.主题

我们在使用Windows操作系统的时候,可以在桌面设置中通过主题来设定当前Windows的UI风格,现在在asp.net 2.0中也新增加了这一功能:主题/皮肤,例如:在我们的系统中,我们可以制作几种样式风格的主题,然后在web.config中,通过<pages>配置项的theme属性来指定要使用的主题,您也可以对单个的页面来进行主题的设定,同时也可以在程序中设定,这样就可以保存并应用用户自己选择的主题了。
  对于asp.net 2.0中自带的webControl都提供了主题的支持,那对于自定义的控件该如何来实现主题呢?
  其实很简单,在要使用主题的属性定义前面,添加属性[Themeable(true)],这样,自定义控件的该属性就能够支持主题了。
  然后,在主题文件夹中,你可以为该自定义控件单独做一个skin文件,也可以在其他的skin文件中指定该自定义控件的属性主题样式等,但您需要在该skin文件中注册该自定义控件,指定其所在程序集、名称空间、标签前缀等,就像我们在页面中使用自定义控件一样,但对于标签前缀的命名不一定要和页面中完全一样。
  对于皮肤的使用同其他asp.net2.0自带控件一样,都是在skin文件中定义多个样式,并设定其SkinId属性,在页面中可以指定该自定义控件的SkinId属性来使用该皮肤,如果没有指定SkinId,则该样式为主题的默认样式。
如下面的样例:
  这是一个用来刷新页面的一个自定义的ImageButton,其好处是刷新当前页,动作的触发是在客户端利用脚本来完成,不需要写任何程序代码:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Web.UI.Design;
using System.Web;
using System.Text;

namespace G3CustomControls
{
    public class RefreshButton:WebControl
    {
        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<img src='" + ImageUrl.Replace("~/", "") + "' style='cursor:hand' onclick='window.location=window.location'>");
        }

        [DefaultValue(""), Themeable(true), Category("Appearance"), Description("RefreshButton_ImageUrl"), UrlProperty, Editor("System.Web.UI.Design.ImageUrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)), Bindable(true)]
        public virtual string ImageUrl
        {
            get
            {
                string imageUrl = (string)this.ViewState["ImageUrl"];
                if (imageUrl != null)
                {
                    return imageUrl;
                }
                return string.Empty;
            }
            set
            {
                this.ViewState["ImageUrl"] = value;
            }
        }
    }
}


主题的Skin文件配置如下:
<%@ Register Assembly="G3CustomControls" Namespace="G3CustomControls" TagPrefix="cc1" %>
  <cc1:CloseButton runat ="server" ImageUrl="~/App_Themes/Blue/images/button_Close.gif"></cc1:CloseButton>
  <cc1:RefreshButton runat ="server" ImageUrl="~/App_Themes/Blue/images/button_Refresh.gif"></cc1:RefreshButton>
  <cc1:RefreshButton runat ="server" SkinId= "RefreshButton2" ImageUrl="~/App_Themes/Blue/images/button_Refresh2.gif"></cc1:RefreshButton>

页面中使用如下:
<%@ Register Assembly="G3CustomControls" Namespace="G3CustomControls" TagPrefix="cc1" %>

……
  <cc1:CloseButton runat ="server"></cc1:CloseButton>
  <cc1:RefreshButton runat ="server"></cc1:RefreshButton>


 其中页面的TagPrefix和Skin的TagPrefix不一定要一样。

 二...................将ASP.NET用户控件转化为自定义控件

1.创建ascx控件,并调试通过

<%@ Control Language="c#" AutoEventWireup="false" Codebehind="WebFtp.ascx.cs" Inherits="Kingsoc.Web.Controls.WebFtp" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<style>A { TEXT-DECORATION: none }
 A:hover { TEXT-DECORATION: underline }
</style>
<P><asp:label id="lblCurrPath" Font-Size="X-Large" runat="server"></asp:label></P>
<P><asp:linkbutton id="btnParent" runat="server">[转到父目录]</asp:linkbutton></P>
<asp:panel id="panAdmin" style="PADDING-RIGHT: 1px; PADDING-LEFT: 1px; PADDING-BOTTOM: 1px; PADDING-TOP: 1px; BORDER-BOTTOM: 0px"
 runat="server" BorderWidth="1px" BorderStyle="Solid" BorderColor="DarkGray" BackColor="DarkGray">
 <asp:Panel id="panInfo" runat="server" BackColor="Yellow" Visible="False" Width="100%">
  <asp:Label id="lblInfo" runat="server" ForeColor="Red"></asp:Label>
 </asp:Panel>
 <asp:Label id="lblUpload" runat="server">上传文件</asp:Label>
 <INPUT class="input" id="file" type="file" name="file" runat="server"/>
 <asp:Button id="btnUpload" runat="server" Width="70px" CssClass="btn" Text="上传"></asp:Button>
 <BR>
 <asp:Label id="lblNewFolder" runat="server">新建目录</asp:Label>
 <asp:TextBox id="txtNewFolder" runat="server" CssClass="input"></asp:TextBox>
 <asp:Button id="btnNewFolder" runat="server" Width="70px" CssClass="btn" Text="新建目录"></asp:Button>
</asp:panel><asp:datagrid id="dg" runat="server" BorderWidth="1px" BorderStyle="Solid" BorderColor="DarkGray"
 BackColor="Transparent" Width="100%" GridLines="None" ShowHeader="False" AutoGenerateColumns="False">
 <Columns>
  <asp:TemplateColumn HeaderText="name">
   <ItemTemplate>
    <asp:LinkButton id="btnName" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "name") %>' CommandName="Select">
    </asp:LinkButton>
   </ItemTemplate>
   <EditItemTemplate>
    <asp:TextBox id="txtName" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "name") %>' CssClass="input">
    </asp:TextBox>
   </EditItemTemplate>
  </asp:TemplateColumn>
  <asp:BoundColumn DataField="type" ReadOnly="True" HeaderText="type"></asp:BoundColumn>
  <asp:BoundColumn DataField="date" ReadOnly="True" HeaderText="date"></asp:BoundColumn>
  <asp:BoundColumn DataField="size" ReadOnly="True" HeaderText="size"></asp:BoundColumn>
  <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="更新" CancelText="取消" EditText="编辑"></asp:EditCommandColumn>
  <asp:ButtonColumn Text="删除" CommandName="Delete"></asp:ButtonColumn>
 </Columns>
</asp:datagrid>
2.将ascx移植为自定义控件

  (一)新建工程aspnet自定义控件工程,将原ascx类中的所有cs代码都拷贝到自定义控件类中去
    (二)修改InitializeComponent()函数以初始化控件。可将其扩充为三个小函数,如: 

3.填充Render()函数用以渲染输出效果。可将其扩展为两个小函数,如 

protected override void Render(HtmlTextWriter output) 

    RenderClientScript(output); 
    RenderServerComponent(output); 

其中 
1
)RenderClientScript函数可用于输出客户端脚本,以及样式style代码,如: 
    
private void
 RenderClientScript(HtmlTextWriter output) 
    { 
        output.Write(
"<style>{0} {1}</style>""A { TEXT-DECORATION: none }""A:hover { TEXT-DECORATION: underline }"
); 
         
    } 
2
)RenderServerComponent函数用于输出各控件,也可在此添加一些普通html代码的输出,如 
    
private void
 RenderServerComponent(HtmlTextWriter output) 
    { 
        
this
.lblCurrPath.RenderControl(output); 
        output.Write(
"<hr width=100% size=1 color=darkgray>"
); 
        
this
.panAdmin.RenderControl(output); 
        
this
.dg.RenderControl(output); 
    } 

            
    (四)让类实现INamingContainer接口,以避免页面上放置多个该控件而导致子控件id冲突的情况
        该接口无任何函数,只需声明实现就可,如

public class WebFtp : System.Web.UI.WebControls.WebControl, INamingContainer 

        只要让类实现该接口,系统将自动实现避免id冲突的方法
        具体步骤是将内部控件id名前都附上父控件的id。如在panel(id=pan)中有一个lable(id=lblName),那么label的id会给自动改为pan_lblName;
     至此,编译调试成功后,该控件就可以使用了。再加上图片,效果可以是这样:
       

三、商业标准化自定义控件 
第二步完成后,该控件已经可以使用了(如何使用?晕,请看附录五),但其还是一个粗胚,例如没有控件图标,没有设计时外观等。
    以下将介绍如何修饰该控件,令其像个标准化的商业控件。
    (1)为控件的属性添加例如类别、描述、默认值等Attribute特性
        这些特性将影响到控件在属性工具栏中的内容
        格式如:[Bindable(true), Category("类别"), Description("描述信息"), DefaultValue("缺省字符串类型值")]
        常用的Attribute有: 

CategoryAttribute               属性类别 
BrowsableAttribute              属性可否显示 
DefaultPropertyAttribute        控件的缺省属性 
DefaultValueAttribute           属性的缺省值 
DescriptionAttribute            属性的描述信息 
ReadOnlyAttribute               属性是否只读 
BindableAttribute               属性是否可绑定 
NotifyParentPropertyAttribute   修改该属性后是否通知控件 

        更多的Attribute 请查阅附录三、附录、以及 System.ComponentModel 命名空间
    (2)添加工具栏图标
        为类添加特性:[ToolboxBitmap(typeof(WebFtp), "Kingsoc.Web.Controls.WebFtp.bmp")]
        其中要注意的是:
        - 项目的默认命名空间为Kingsoc.Web.Controls
        - 图片名称为webftp.bmp,并需要设置其编译选项为“嵌入的资源”
        这样工具栏上图标就变成这样咯~:
    (3)添加设计时外观支持
        为类添加特性:[Designer(typeof(Kingsoc.Web.Controls.WebFtpDesigner))]
        其中,类 Kingsoc.Web.Controls.WebFtpDesigner 继承至ControlDesigner,用于绘制设计时的控件外观,如: 

        using System; 
        
using System.Web.UI; 
        
using System.Web.UI.WebControls; 
        
using System.ComponentModel; 
        
using System.ComponentModel.Design; 
        
using System.Web.UI.Design; 
        
using System.Text; 
        
namespace Kingsoc.Web.Controls 
        { 
         
public class WebFtpDesigner : ControlDesigner 
         { 
                
public override string GetDesignTimeHtml() 
                { 
                    
//return base.GetDesignTimeHtml (); 
                    WebControl ctrl = (WebControl)this.Component; 
                    StringBuilder sb 
= new StringBuilder(); 
                    sb.AppendFormat( 
                        
"<label style='align:center; valign:middle; font-size:small; font-family: Arial; background-color: #eeeeee; padding:2px; border:2px dotted gray; width:{0}; height:{1};'><center>{2}</center></lable>"
                        ctrl.Width, 
                        ctrl.Height, 
                        ctrl.ID); 
                    
return sb.ToString(); 
                } 
        
                
public override bool AllowResize 
                { 
                    
get {return true;} 
                } 
         } 
        } 

        这样,设计时控件就有了外观支持:
        (嫌太简陋?自己用dreamweaver等设计工具设计,想多好看就多好看!再拷贝html代码到GetDesignTimeHtml()函数内部去)
     (4)添加TagPrefix
        用于指定将控件从工具栏中拖放到窗体中,自动生成的控件名称前缀(实际上是控件所属的命名空间的别名)
        在AssemblyInfo.cs文件中添加:[assembly: TagPrefix("Kingsoc.Web.Controls", "ks")]
        这样,添加了该控件的web窗体就会自动生成类似代码

<%@ Register TagPrefix="ks" Namespace="Kingsoc.Web.Controls" Assembly="Kingsoc.Web.Controls.WebFtp" %>
<ks:WebFtp id="webFtp" runat="server"></ks:WebFtp>

     (5)设置版权信息
        修改AssemblyInfo.cs文件中的以下特性 

[assembly: AssemblyTitle("")] 
[assembly: AssemblyDescription(
"")] 
[assembly: AssemblyConfiguration(
"")] 
[assembly: AssemblyCompany(
"Kingsoc software")] 
[assembly: AssemblyProduct(
"")] 
[assembly: AssemblyCopyright(
"")] 
[assembly: AssemblyTrademark(
"Kingsoc")] 
[assembly: AssemblyCulture(
"")] 

     (6)将控件添加到GAC中。见附件五
     (7)添加控件注册保护。见附件六 
     


--------------------------------------------------------------------       
附录
--------------------------------------------------------------------    
附录一:自定义控件基类的选择 
    Control     所有web控件的基类
    WebControl  继承至Control类,并添加了例如Width,Height等属性,这是常用且默认的基类,常用于有外观的控件
    Component   常用于无外观的控件,例如:数据访问控件
    故,对于组合控件,一般使用WebControl作为基类。而对于无界面的控件,一般使用Component就可以了

附录二:用代码创建DataGrid绑定列 
    (1)普通绑定列 

BoundColumn col = new BoundColumn(); 
col.DataField 
= "name"
col.ReadOnly 
= true
dg.Columns.Add(col); 

    (2)模板绑定列 

TemplateColumn col = new TemplateColumn(); 
col.ItemTemplate 
= new LinkButtonTemplate("btnName""name""Select"); 
col.EditItemTemplate 
= new TextBoxTemplate("txtName""name"); 
dg.Columns.Add(col); 
其中LinkButtonTemplate和TextBoxTemplate模板代码如下: 
// LinkButton模板 
public class LinkButtonTemplate : ITemplate 

    
string _id; 
    
string _bindField; 
    
string _commandName; 

    
public LinkButtonTemplate(string id, string bindField, string commandName) 
    { 
        _id 
= id; 
        _commandName 
= commandName; 
        _bindField 
= bindField; 
    } 
    
public void InstantiateIn(Control container) 
    { 
        LinkButton ctrl 
= new LinkButton(); 
        ctrl.ID 
= _id; 
        ctrl.CommandName 
= _commandName; 
        ctrl.DataBinding 
+= new EventHandler(this.BindData); 
        container.Controls.Add(ctrl); 
    } 
    
void BindData(object sender, System.EventArgs e) 
    { 
        LinkButton ctrl 
= sender as LinkButton; 
        ctrl.Text 
= DataBinder.Eval(ctrl.NamingContainer, "DataItem." + _bindField).ToString(); 
        
//或 ctrl.Text = ((ctrl.NamingContainer as DataGridItem).DataItem as DataRowView)[_bindField].ToString(); 
    } 


// TextBox模板 
public class TextBoxTemplate : ITemplate 

    
string _id; 
    
string _bindField; 

    
public TextBoxTemplate(string id, string bindField) 
    { 
        _id 
= id; 
        _bindField 
= bindField; 
    } 
    
public void InstantiateIn(Control container) 
    { 
        TextBox ctrl 
= new TextBox(); 
        ctrl.ID 
= _id; 
        ctrl.DataBinding 
+= new EventHandler(this.BindData); 
        container.Controls.Add(ctrl); 
    } 
    
void BindData(object sender, System.EventArgs e) 
    { 
        TextBox ctrl 
= sender as TextBox; 
        ctrl.Text 
= DataBinder.Eval(ctrl.NamingContainer, "DataItem." + _bindField).ToString(); 
    } 



附录三:自定义控件可用的特性(Attribute) 
    System.ComponentModel 命名空间提供用于实现组件和控件运行时和设计时行为的类。
    此命名空间包括用于实现属性和类型转换器、绑定到数据源以及授权组件的基类和接口。
        CategoryAttribute
        BrowsableAttribute
        DefaultPropertyAttribute
        DefaultValueAttribute
        DescriptionAttribute
        ReadOnlyAttribute
        NotifyParentPropertyAttribute
       
        AmbientValueAttribute
        BindableAttribute
        DefaultEventAttribute
        DesignerAttribute
        DesignerCategoryAttribute
        DesignerSerializationVisibilityAttribute
        DesignOnlyAttribute
        DesignTimeVisibleAttribute
        EditorBrowsableAttribute
        ImmutableObjectAttribute
        InheritanceAttribute
        InstallerTypeAttribute
        LicenseProviderAttribute
        ListBindableAttribute
        MergablePropertyAttribute
        ParenthesizePropertyNameAttribute
        ProvidePropertyAttribute
        RecommendedAsConfigurableAttribute
        RefreshPropertiesAttribute
        RunInstallerAttribute
        ToolboxItemAttribute
        ToolboxItemFilterAttribute
        TypeConverterAttribute 
    
   
附录四:关于DefaultValue特性
    只用于检测用户输入值是否与该值相同,若相同,则不会在asp页面代码中显示出该属性值;反之则显示。实际并没有修改该属性的值!
    例如,对于以下语句,AttachTimeStampToUploadedFile 应该等于构造函数中指定的true,而非DefaultValue中提供的false 

[DefaultValue(false)] 
public bool AttachTimeStampToUploadedFile 

    
get {return _AttachTimeStampToUploadedFile;} 
    
set {_AttachTimeStampToUploadedFile = value;} 

public WebFtp() 

    AttachTimeStampToUploadedFile 
= true

    所以,设置默认值时应该确保与初始化值相同,否则会带来令人困惑的结果。要么干脆就不写。

附录五:如何将自定义控件添加到工具栏 
    (1)在工具栏面板中点击右键,点击”添加/移除项..."
    (2)在弹出的“自定义工具箱”对话框中点击“浏览”按钮,选择自定义控件dll文件
    (3)点击确定,关闭对话框。控件图标将显示在工具栏面板上

附录六:将控件添加到GAC中 
     未完成

附录七:添加控件注册保护 

private void InitializeComponent()  
{  
    CreateComponent();  
    SetComponentProperty();  
    SetComponentEvent();  
}  
其中  
1)CreateComponent()函数用于新建出所有使用到的控件,并组织好其层次关系  
    
private void CreateComponent()  
    {  
        
// create  
        lblCurrPath = new Label();  
        lblUpload 
= new Label();  
        lblNewFolder 
= new Label();  
        
          
        
// nest  
        Controls.Add(this.lblCurrPath);  
        Controls.Add(
new HtmlGenericControl("br"));  
        
this.panAdmin.Controls.Add(this.file);  
        
    }  
2)SetComponentProperty()函数用于设置这些控件的属性,如  
    
private void SetComponentProperty()  
    {  
        lblCurrPath.Font.Size 
= FontUnit.XLarge;  
        lblCurrPath.CssClass 
= "webftp_title";  
        panInfo.BackColor 
= Color.Yellow;  
        panInfo.Visible 
= false;  
        
    }
    值得注意的是,aspnet提供的组合绑定控件(如DataGrid, DataList..)的属性设置较为复杂,参见附录二 
3)SetComponentEvent()函数中设置控件的事件句柄,这个从原有的InitializeComponent函数中拷贝过来就可,如 
private void SetComponentEvent()  
{  
    
this.btnUpload.Click += new System.EventHandler(this.btnUpload_Click);  
    
this.btnNewFolder.Click += new System.EventHandler(this.btnNewFolder_Click);  
    
this.dg.ItemCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.dg_ItemCommand);  
    
this.Load += new System.EventHandler(this.Page_Load);  
    

原创粉丝点击