如有不明白的地方欢迎加QQ群14670545 探讨
简单的自定义控件三
在学习了控件的生命周期之后,我们来进一步处学习。这节我们把简单的js判断放到控件里面,同时对不可用的控件要禁止其提交行为。
好的,首先分析下,我怎么知道页面上那些是我自定义的控件呢,GetType()方法或者is xxx类型 都可以用来判断,但是我必须要知道GetType()之后要与哪种类型匹配,is也是一样。基于这种思考,我们就需要来一个接口(interface)以方便我们做某些功能的统一处理。
1.新建类库CustomerWebControls,新建类ICustomControl.cs,写入代码如下:
namespace CustomerWebControls
{
///
/// 自定义控件的统一接口
///
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
{
///
/// 是否验证输入的合法性
///
[Bindable(true), Category("Appearance"), DefaultValue(true), Localizable(true)]//在属性窗口中是否可见
public bool IsValidata
{
get { return ViewState["IsValidata"] != null ? (bool)ViewState["IsValidata"] : false; }
set { ViewState["IsValidata"] = value; }
}
///
/// 是否移除不安全字符串
///
[Bindable(true), Category("Appearance"), DefaultValue(true), Localizable(true)]
public bool IsRemoveUnsafeHtml
{
set { ViewState["IsRemoveUnsafeHtml"] = value; }
get { return ViewState["IsRemoveUnsafeHtml"] != null ? (bool)ViewState["IsRemoveUnsafeHtml"] : true; }
}
///
/// 重写TextBox的Text属性
///
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("");
}
}
}
这里的IsValidata属性我们是为了下一节把客户端脚本(比如jquery ui)加进来处理而提前写的(此篇先不说了,看下篇吧 )
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";
///
/// 过滤HTML中的不安全标签
///
/// 目标字符
/// 移除不合法的字符
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';";
到此我们的重绘一个简单的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
{
///
/// 默认的构造函数。
///
public CCButton()
{
base.Text = "保存";
this.ViewState["afterSubmitText"] = "提交成功";
this.ViewState["checkclient"] = false;
this.ViewState["beforeSubmitText"] = "确定要提交吗?";
}
///
/// 获取或设置单击按钮后,按钮上所显示的文本。
///
[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);
}
///
/// 处理回传事件
///
///
protected override void RaisePostBackEvent(string eventArgument)
{
Alert(AfterSubmitText);
base.RaisePostBackEvent(eventArgument);
}
///
/// 控件的属性添加到输出流用以在客户端上呈现内容
/// 重写button按钮的AddAttributesToRender
///
/// HtmlTextWriter:其中包含要在客户端上呈现内容的输出流
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);
}
///
/// 在客户端显示弹出对话框。
///
/// 要显示的信息。
public void Alert(string msg)
{
Alert("alert", msg);
}
///
/// 在客户端显示弹出对话框。
///
/// 脚本块标识。当同一页面要调用两个弹出框时需不同的标识,否则后者会覆盖前者。
/// 要显示的信息。
public void Alert(string name, string msg)
{
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), name, "");
}
}
}
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ccPage.aspx.cs" Inherits="MyWebSiteTest.Manager.ccPage" %>
<%@ Register Assembly="CustomerWebControls" Namespace="CustomerWebControls" TagPrefix="cc1" %>
测试封装的控件
我们在后台做个简单的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
BasePage:
using System.Web.UI;
namespace MyWebSiteTest
{
public class BasePage : System.Web.UI.Page
{
///
/// 禁止客户端失效按钮提交
/// RaisePostBackEvent通知引起回发的服务器控件:它应处理传入的回发事件
///
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
{
if (!(bool)sourceControl.GetType().GetProperty("Enabled").GetValue(sourceControl, null))
return;
base.RaisePostBackEvent(sourceControl, eventArgument);
}
}
}
using System;
using System.Collections.Generic;
using System.Web.UI;
using CustomerWebControls;//我们自定义控件的类库
namespace MyWebSiteTest
{
public class PageUI : BasePage
{
///
/// 控件列表
///
private readonly List _CustomControl;
///
/// 构造
///
public PageUI()
{
_CustomControl = new List();
}
///
/// 获取自定义服务器控件
///
///
private void GetCustomServerButtons(ControlCollection cc)
{
foreach (Control c in cc)
{
if (c is ICustomControl)
_CustomControl.Add(c);
else if (c.HasControls())
GetCustomServerButtons(c.Controls);//调用自身(伪递归)
}
}
///
/// 做一些处理咯
/// 可自由发挥处理,比如权限方面的处理
///
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;
}
}
}
///
/// 在页初始化后引发
///
protected override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
GetCustomServerButtons(Controls);
InitControlsRight();
}
}
}
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