您的位置:首页 > 其它

简单的自定义控件四简单的自定义控件四

2013-01-09 10:55 369 查看
如有不明白的地方欢迎加QQ群14670545 探讨

简单的自定义控件三

在学习了控件的生命周期之后,我们来进一步处学习。这节我们把简单的js判断放到控件里面,同时对不可用的控件要禁止其提交行为。

好的,首先分析下,我怎么知道页面上那些是我自定义的控件呢,GetType()方法或者is xxx类型 都可以用来判断,但是我必须要知道GetType()之后要与哪种类型匹配,is也是一样。基于这种思考,我们就需要来一个接口(interface)以方便我们做某些功能的统一处理。

1.新建类库CustomerWebControls,新建类ICustomControl.cs,写入代码如下:

namespace CustomerWebControls
{
    /// <summary>
    /// 自定义控件的统一接口
    /// </summary>
    public interface ICustomControl
    {
        //...
    }
}
ICustomControl这里面我们没有写什么,空着的,后面我们会讲到比如控件的权限,到时候需要修改我们的接口(后话了),好了现在我们自定义的控件要是继承了ICustomControl接口,那么我们可以认为它属于ICustomControl类型(标准)的规格控件

2.来一个我们来绘一个textbox,前面章节有讲到,这里就不说了,直接上代码:

using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CustomerWebControls
{
    [DefaultProperty("IsValidata"), ToolboxData("<{0}:CCTextBox runat=server />")]
    public class CCTextBox : TextBox, ICustomControl
    {
        /// <summary>
        /// 是否验证输入的合法性
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(true), Localizable(true)]//在属性窗口中是否可见
        public bool IsValidata
        {
            get { return ViewState["IsValidata"] != null ? (bool)ViewState["IsValidata"] : false; }
            set { ViewState["IsValidata"] = value; }
        }
        /// <summary>
        /// 是否移除不安全字符串
        /// </summary>
        [Bindable(true), Category("Appearance"), DefaultValue(true), Localizable(true)]
        public bool IsRemoveUnsafeHtml
        {
            set { ViewState["IsRemoveUnsafeHtml"] = value; }
            get { return ViewState["IsRemoveUnsafeHtml"] != null ? (bool)ViewState["IsRemoveUnsafeHtml"] : true; }
        }

        /// <summary>
        /// 重写TextBox的Text属性
        /// </summary>
        public override string Text
        {
            get { return IsRemoveUnsafeHtml ? CCTools.RemoveUnsafeHtml(base.Text.Trim()) : base.Text.Trim(); }
            set { base.Text = value; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            if (string.IsNullOrEmpty(CssClass))
            {
                CssClass = "txt";
                Attributes["onmouseover"] = "this.className='colorfocus';";
                Attributes["onfocus"] = "this.className='colorfocus';";
                Attributes["onmouseout"] = "this.className='colorblur';";
                Attributes["onblur"] = "this.className='colorblur';";
            }

            base.OnPreRender(e);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            base.Render(writer);
            if (IsValidata)//需要验证,就添加一个js提示
                writer.Write("<span id=\"" + ClientID + "Tip\"></span>");
        }
    }
}
这里的IsValidata属性我们是为了下一节把客户端脚本(比如jquery ui)加进来处理而提前写的(此篇先不说了,看下篇吧



CCTools.cs类的代码如下:

namespace CustomerWebControls
{
    public class CCTools
    {
        private const string StrKeyWord = @"select|insert|delete|from|drop table|update|truncate|xp_cmdshell|exec master|netlocalgroup administrators|:|net user";
        /// <summary>
        /// 过滤HTML中的不安全标签
        /// </summary>
        /// <param name="content">目标字符</param>
        /// <returns>移除不合法的字符</returns>
        public static string RemoveUnsafeHtml(string content)
        {
            content = content.Replace("'", "").Replace("%", "").ToLower();
            string[] arry_sql = StrKeyWord.Split('|');
            foreach (string paramSQL in arry_sql)
                if (content.IndexOf(paramSQL) > -1)
                    content = content.Replace(paramSQL, "$$");

            return content;
        }
    }
}
基于这个CCTextBox的

CssClass = "txt";
                Attributes["onmouseover"] = "this.className='colorfocus';";
                Attributes["onfocus"] = "this.className='colorfocus';";
                Attributes["onmouseout"] = "this.className='colorblur';";
                Attributes["onblur"] = "this.className='colorblur';";


我们需要在页面上写一些css以配合效果

<style type="text/css">
        .txt {  
	        border-top-width: 1px;
	        padding-right: 1px; 
	        padding-left: 1px;
	        padding-bottom: 1px; 
	        padding-top: 1px; 
	        border-left-width: 1px; 
	        border-bottom-width: 1px; 
	        border-right-width: 1px;
	        
	        border-left-color: #707070; 
	        border-bottom-color: #CECECE;
	        border-top-color: #707070; 
	        border-right-color: #CECECE;	        
	        font-family: Tahoma, Verdana, "宋体"; 
	        font-size: 12px; 
	        color: #000000; 
	        background-color: #FFFFFF;
	        height:18px; line-height:18px;
        }
        .colorfocus {
	        border-width:1px;
            border-style:solid;
	        border-left-color: #069; 
	        border-bottom-color: #C2D5E3;
	        border-top-color: #069; 
	        border-right-color: #C2D5E3;
	        
	        background-color: #EBF2F6;
	        height:18px; line-height:18px;
        }
        .colorblur 
        {
            border-width:1px;
            border-style:solid;
	        border-left-color: #707070; 
	        border-bottom-color: #CECECE;
	        border-top-color: #707070; 
	        border-right-color: #CECECE;
	        background-color: #ffffff;
	        height:18px; line-height:18px;
        }
    </style>


到此我们的重绘一个简单的TextbOX结束,下面我们来处理button按钮:

2.在CustomerWebControls类库里面新建一个类CCButton.cs,同样,我们也让它继承ICustomControl接口,达到统一标准。

还记得我们上一章讲的web服务器控件的生命周期吗,这里我们就需要用到啦。在按钮提交的时候一般我们会做一些客户端的脚本提示,不如在OnClientClick事件里面

return checkInfo();一下。这个checkInfo是个客户端的js函数,当然它是我们自己写的一些东西。这里我们不妨用一个属性来指代当前自定义的button是否需要添加一个这样的提示,这时候我们可以重写button的AddAttributesToRender事件,它是用来将控件的属性添加到输出流用以在客户端上呈现内容,这里我们就可以写一些脚本判断。

当然,当我们的按钮向服务器提交完以后,或许还要做一些提示什么的,这个时候可以对回发事件进行重写。按照上面的想法,现在我们来处理代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.ComponentModel;
using System.Web.UI.WebControls;

namespace CustomerWebControls
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:CCButton runat=server></{0}:CCButton>")]
    public class CCButton : Button, ICustomControl
    {
        /// <summary>
        /// 默认的构造函数。
        /// </summary>
        public CCButton()
        {
            base.Text = "保存";
            this.ViewState["afterSubmitText"] = "提交成功";
            this.ViewState["checkclient"] = false;
            this.ViewState["beforeSubmitText"] = "确定要提交吗?";
        }

        /// <summary>
        /// 获取或设置单击按钮后,按钮上所显示的文本。
        /// </summary>
        [Bindable(true),
        Category("Appearance"),
        DefaultValue("提交成功"),
        Description("指示单击提交后,按钮回发事件弹出的提示信息。")]
        public string AfterSubmitText
        {
            get
            {
                string afterSubmitText = (string)this.ViewState["afterSubmitText"];
                if (afterSubmitText != null)
                {
                    return afterSubmitText;
                }
                else
                {
                    return string.Empty;
                }
            }
            set
            {
                this.ViewState["afterSubmitText"] = value;
            }
        }

        [Bindable(true),
        Category("Appearance"),
        DefaultValue(false),
        Description("指示是否要显示一个提示框。")]
        public bool CheckClient
        {
            get
            {
                return (bool)this.ViewState["checkclient"];
            }
            set
            {
                this.ViewState["checkclient"] = value;
            }
        }

        [Bindable(true),
        Category("Appearance"),
        DefaultValue("确定要提交吗?"),
        Description("指示提示框内所包含的内容。")]
        public string BeforeSubmitText
        {
            get
            {
                return (string)this.ViewState["beforeSubmitText"];
            }
            set
            {
                this.ViewState["beforeSubmitText"] = value;
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            base.Render(writer);
        }

        /// <summary>
        /// 处理回传事件
        /// </summary>
        /// <param name="eventArgument"></param>
        protected override void RaisePostBackEvent(string eventArgument)
        {
            Alert(AfterSubmitText);
            base.RaisePostBackEvent(eventArgument);
        }

        /// <summary>
        /// 控件的属性添加到输出流用以在客户端上呈现内容
        /// <remarks>重写button按钮的AddAttributesToRender</remarks>
        /// </summary>
        /// <param name="writer">HtmlTextWriter:其中包含要在客户端上呈现内容的输出流</param>
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            System.Text.StringBuilder ClientSideEventReference = new System.Text.StringBuilder();

            if (((this.Page != null) && this.CausesValidation) && (this.Page.Validators.Count > 0))
                ClientSideEventReference.Append("if (typeof(Page_ClientValidate) == 'function'){if (Page_ClientValidate() == false){return false;}}");
            if (this.CheckClient)
                ClientSideEventReference.Append("if (!confirm('" + this.BeforeSubmitText + "')){return false}");

            ClientSideEventReference.Append("this.disabled = true;");
            ClientSideEventReference.Append(this.Page.ClientScript.GetPostBackEventReference(this, string.Empty));

            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, ClientSideEventReference.ToString(), true);
            base.AddAttributesToRender(writer);
        }

        /// <summary>
        /// 在客户端显示弹出对话框。
        /// </summary>
        /// <param name="msg">要显示的信息。</param>
        public void Alert(string msg)
        {
            Alert("alert", msg);
        }
        /// <summary>
        /// 在客户端显示弹出对话框。
        /// </summary>
        /// <param name="name">脚本块标识。当同一页面要调用两个弹出框时需不同的标识,否则后者会覆盖前者。</param>
        /// <param name="msg">要显示的信息。</param>
        public void Alert(string name, string msg)
        {
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), name, "<script language=\"javascript\">alert('" + msg + "');</script>");
        }
    }
}


到此我们生成一下,然后web层引用,再把两个控件拖到页面上去看看

<cc1:CCTextBox ID="CCTextBox1" runat="server" ></cc1:CCTextBox>
<cc1:CCButton ID="CCButton1" runat="server" />


为了好看和测下事件的效果,我们添加一下属性,最终页面代码:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ccPage.aspx.cs" Inherits="MyWebSiteTest.Manager.ccPage" %>

<%@ Register Assembly="CustomerWebControls" Namespace="CustomerWebControls" TagPrefix="cc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>测试封装的控件</title>
<style type="text/css"> .txt { border-top-width: 1px; padding-right: 1px; padding-left: 1px; padding-bottom: 1px; padding-top: 1px; border-left-width: 1px; border-bottom-width: 1px; border-right-width: 1px; border-left-color: #707070; border-bottom-color: #CECECE; border-top-color: #707070; border-right-color: #CECECE; font-family: Tahoma, Verdana, "宋体"; font-size: 12px; color: #000000; background-color: #FFFFFF; height:18px; line-height:18px; } .colorfocus { border-width:1px; border-style:solid; border-left-color: #069; border-bottom-color: #C2D5E3; border-top-color: #069; border-right-color: #C2D5E3; background-color: #EBF2F6; height:18px; line-height:18px; } .colorblur { border-width:1px; border-style:solid; border-left-color: #707070; border-bottom-color: #CECECE; border-top-color: #707070; border-right-color: #CECECE; background-color: #ffffff; height:18px; line-height:18px; } </style>
</head>
<body>
<form id="form1" runat="server">
<div style=" margin:0 auto; width:600px;">
<cc1:CCTextBox ID="CCTextBox1" runat="server" Width="200" IsValidata="true" ></cc1:CCTextBox>
<cc1:CCButton ID="CCButton1" runat="server" CheckClient="true" OnClick="btnTest_Click" />
</div>
</form>
</body>
</html>
我们在后台做个简单的btnTest_Click事件,为什么要添加这个事件,因为我们需要测试防止页面刷新之后重复提交,等会你就看到效果了,刷新页面(如果页面不跳转,这时候刷新还是会提示的,因为页面重绘了,跳转之后返回来刷新时不会提示的)也不会提交的。

protected void btnTest_Click(object sender, EventArgs e)
 {
     Response.Redirect("http://www.baidu.com");
 }
好了,基本已经完成,但是我们一开始说了,对于那些失效的按钮,我们要保证它不能提交事件的(比如winform软件,某些按钮是失效的,我们可以用一些外挂插件让失效的按钮的可用,完全是可以做到),为了以防万一我们需要做这样的处理。同时,我们也说了,我怎么获取页面上我自己写的ICustomControl标准接口的控件呢,基于这些考虑,我们需要些2个基类,一个最终的BasePage(它继承于System.Web.UI.Page),一个中间层的PageUI(它继承于BasePage),我们再让页面类的ccPage.cs

继承PageUI。这样做的好处事,我们可以在PageUI里面处理我自定义控件的一些事(比如权限控制,比如根据权限是否只读等等),在基类BasePage我们可以统一处理符合某一个情况下的所有控件行为(比如让所有不可用的控件全部不能做提交处理)。好了,我们来写代码:

BasePage:

using System.Web.UI;

namespace MyWebSiteTest
{
    public class BasePage : System.Web.UI.Page
    {
        /// <summary>
        /// 禁止客户端失效按钮提交
        /// <remarks>RaisePostBackEvent通知引起回发的服务器控件:它应处理传入的回发事件</remarks>
        /// </summary>
        protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
        {
            if (!(bool)sourceControl.GetType().GetProperty("Enabled").GetValue(sourceControl, null))
                return;
            base.RaisePostBackEvent(sourceControl, eventArgument);
        }        
    }
}


PageUI:

using System;
using System.Collections.Generic;
using System.Web.UI;
using CustomerWebControls;//我们自定义控件的类库

namespace MyWebSiteTest
{
    public class PageUI : BasePage
    {
        /// <summary>
        /// 控件列表
        /// </summary>
        private readonly List<Control> _CustomControl;

        /// <summary>
        /// 构造
        /// </summary>
        public PageUI()
        {
            _CustomControl = new List<Control>();
        }

        /// <summary>
        /// 获取自定义服务器控件
        /// </summary>
        /// <param name="cc"></param>
        private void GetCustomServerButtons(ControlCollection cc)
        {
            foreach (Control c in cc)
            {
                if (c is ICustomControl)
                    _CustomControl.Add(c);
                else if (c.HasControls())
                    GetCustomServerButtons(c.Controls);//调用自身(伪递归)
            }
        }

        /// <summary>
        /// 做一些处理咯
        /// <remarks>可自由发挥处理,比如权限方面的处理</remarks>
        /// </summary>
        private void InitControlsRight()
        {
            foreach (Control obj in _CustomControl)
            {
                switch (obj.GetType().Name)
                {
                    case "CCTextBox":
                        CCTextBox cctextbox = obj as CCTextBox;
                        if (cctextbox != null && cctextbox.Enabled)
                            cctextbox.ReadOnly = false;
                        break;
                }
            }
        }
        /// <summary>
        /// 在页初始化后引发
        /// </summary>
        protected override void OnInitComplete(EventArgs e)
        {
            base.OnInitComplete(e);
            GetCustomServerButtons(Controls);
            InitControlsRight();
        }
    }
}


ccPage(页面后台):

using System;

namespace MyWebSiteTest.Manager
{
    public partial class ccPage : PageUI
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnTest_Click(object sender, EventArgs e)
        {
            Response.Redirect("http://www.baidu.com");
        }
    }
}


生成一些,运行效果

①效果



②按钮单击效果



③服务器回传页面跳转了,在把页面返回来刷新下看看



页面不会提示,但是当我们把

//Response.Redirect("http://www.baidu.com");


注释掉,再测试一下:先弹窗



刷新测试一下会有提示



OK,到此结束,下一节我们把客户端的js脚本放进来。做一个ui比较好的demo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐