第4课:ASP.NET(4)——自定义控件
虽然ASP.NET Web项目中可以使用的Web控件很多,但在一些特殊情况下还是需要自定义一些控件,本节将讨论两种创建自定义控件的方法,分别是使用C#类和.ascx文件。
HTML5日期和时间控件(基于C#类)
HTML5标准中,input元素又增加了一些类型,如显示和输入日期的date属性;那么,在ASP.NET Web项目中,我们如何使用此属性创建构建自己的日期控件呢?
如下面的代码(/app_code/aspnet/ctr/CtrDate.cs),我们利用WebControl类作为基类创建CtrDate控件。
C# |
using System;
using System.Collections.Specialized;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace acl.aspnet.ctr
{
[ControlValueProperty("Value")]
[ValidationProperty("Value")]
public class CtrDate : WebControl, IPostBackDataHandler
{
public string Value
{
get
{
return (string)ViewState["Value"];
}
set
{
ViewState["Value"] = value;
}
}
public virtual bool LoadPostData(string postDataKey,
NameValueCollection postCollection)
{
string presentValue = Value;
string postedValue = postCollection[postDataKey];
if (presentValue == null || !presentValue.Equals(postedValue))
{
Value = postedValue;
return true;
}
return false;
}
public virtual void RaisePostDataChangedEvent()
{
//
}
protected override void Render(HtmlTextWriter output)
{
output.Write("<input type=date id=" + UniqueID + " name=" + UniqueID
+ " value=" + Value + ">");
}
}
}
|
代码中,我们将CtrDate类定义在acl.aspnet.ctr命名空间,在注册控件时需要指定控件所在的命名空间,稍后可以看到如何在Web.config文件中注册控件。
这里,通过继承WebControl类,并实现 IPostBackDataHandler接口创建了CtrDate控件;其中实现了一些基础的控件成员,如Value属性等。
显示日期控件的关键在Render()方法,这里定义了页面中呈现控件的代码,其中定义了一个input元素,id和name属性定义为控件的唯一ID,type属性定义为date,value属性定义为Text属性。
设置type属性为date值的input元素值(value属性)时,需要使用标准的“YYYY-MM-DD”格式,即使用4位年份、2位月份和2位日期,当年份不足4位,月份和日期不足2位时需要使用前导0。在tDate类中定义了DateTime类型的扩展方法DateStr(),用于返回“YYYY-MM-DD”格式的年、月、日字符串,如下面的代码(/app_code/common/base/tDate.cs)。
C# |
using System;
public static class tDate
{
// 标准8位年、月、日格式
public static string DateStr(this DateTime dt)
{
return string.Format("{0:d4}-{1:d2}-{2:d2}", dt.Year, dt.Month, dt.Day);
}
// 其它代码...
}
|
在ASP.NET Web项目中使用C#类定义的控件时,首先需要注册,如在Web.config配置文件的pages节点中添加如下代码。
XML |
<pages controlRenderingCompatibilityVersion="4.0">
<controls>
<add tagPrefix="acl" namespace="acl.aspnet.ctr" />
</controls>
</pages>
|
controls节点中使用add节点添加C#类定义的控件,这里只需要使用tagPrefix属性指定控件前缀,namespace属性指定控件所在的命名空间,页面中可以使用“<前缀>:<类名>”的格式定义控件。
接下来,我们通过/demo/ctr/CtrDateDemo.aspx页面测试CtrDate控件的使用,首先是HTML部分,如下面的代码。
HTML |
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="CtrDateDemo.aspx.cs" Inherits="demo_ctr_CtrDateDemo" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<p>
<acl:CtrDate ID="date1" runat="server" />
</p>
<p>
<acl:CtrDate ID="date2" runat="server" />
</p>
<p>
<asp:TextBox ID="txt1" runat="server"></asp:TextBox>
</p>
<p>
<asp:Button ID="btn1" runat="server" Text="测试一" OnClick="btn1_Click" />
<asp:Button ID="btn2" runat="server" Text="测试二" OnClick="btn2_Click" />
</p>
</form>
</body>
</html>
|
代码中定义了一个CtrDate控件(date1)、一个文本控件(txt1)和一个按钮控件(btn1)。页面中,CtrDate控件显示为type属性是date的input控件,可以弹出日历选择日期,如图1(Google Chrome浏览器)。
图1
下面是页面的C#代码部分(/demo/ctr/CtrDateDemo.aspx.cs)。
C# |
using System;
public partial class demo_ctr_CtrDateDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false)
{
date1.Value = DateTime.Now.DateStr();
}
}
protected void btn1_Click(object sender, EventArgs e)
{
date2.Value = date1.Value;
}
protected void btn2_Click(object sender, EventArgs e)
{
txt1.Text = date1.Value;
}
}
|
页面首次打开时,date1控件会显示系统当前日期,点击btn1控件时,date2控件中会显示date1中的日期,点击btn2按钮时会在txt1控件中会显示date2控件中的日期值,;如果CtrDate控件中没有选中日期,则Text属性会返回空字符串。页面操作效果如图2。
图2
HTML5标准中的input元素,当type属性设置为time时,可以定义为时间控件,这里封装为CtrTime控件,如下面的代码(/app_code/aspnet/ctr/CtrTime.cs)。
C# |
using System;
using System.Collections.Specialized;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace acl.aspnet.ctr
{
[ControlValueProperty("Value")]
[ValidationProperty("Value")]
public class CtrTime : WebControl, IPostBackDataHandler
{
public string Value
{
get
{
return (string)ViewState["Value"];
}
set
{
ViewState["Value"] = value;
}
}
public virtual bool LoadPostData(string postDataKey,
NameValueCollection postCollection)
{
string presentValue = Value;
string postedValue = postCollection[postDataKey];
if (presentValue == null || !presentValue.Equals(postedValue))
{
Value = postedValue;
return true;
}
return false;
}
public virtual void RaisePostDataChangedEvent()
{
//
}
protected override void Render(HtmlTextWriter output)
{
output.Write("<input type=time id=" + UniqueID + " name=" + UniqueID
+ " value=" + Value + ">");
}
}
}
|
可以看到,CtrTime控件与CtrDate控件的创建非常相似,需要注意的是Render()方法中,呈现到客户端的input元素中的type属性应该设置为time。
实现应用中,CtrTime控件的Text属性数据格式为"hh:mm",即使用两位小时数据和两位分钟数据。Firefox浏览器中显示的内容包括上午或下午,而Google Chrome浏览器则直接使用24小时格式。
为方便获取标准格式的时间文本,在tDate类中定义了相应的扩展方法,如下面的代码(/app_code/common/base/tDate.cs)。
C# |
public static string HourMinuteStr(this DateTime dt)
{
return string.Format("{0:d2}:{1:d2}", dt.Hour, dt.Minute);
}
|
我们使用/demo/ctr/CtrTimeDemo.aspx页面测试CtrTime控件的使用,其中HTML代码部分如下。
HTML |
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="CtrTimeDemo.aspx.cs" Inherits="demo_ctr_CtrTimeDemo" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<p>
<acl:CtrTime ID="time1" runat="server" />
</p>
<p>
<acl:CtrTime ID="time2" runat="server" />
</p>
<p>
<asp:TextBox ID="txt1" runat="server"></asp:TextBox>
</p>
<p>
<asp:Button ID="btn1" Text="测试一" runat="server" OnClick="btn1_Click" />
<asp:Button ID="btn2" Text="测试二" runat="server" OnClick="btn2_Click" />
</p>
</form>
</body>
</html>
|
页面的C#代码部分如下。
C# |
using System;
public partial class demo_ctr_CtrTimeDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack == false)
{
time1.Value = DateTime.Now.HourMinuteStr();
}
}
protected void btn1_Click(object sender, EventArgs e)
{
time2.Value = time1.Value;
}
protected void btn2_Click(object sender, EventArgs e)
{
txt1.Text = time1.Value;
}
}
|
首次打开页面时,time1控件会显示系统当前时间(时、分),点击btn1按钮时,time2控件会显示time1中的时间,点击btn2按钮时,txt1文本框会显示时间的文本内容,执行效果如图3。
图3
页眉和页脚控件(基于.ascx)
另一种创建控件的方法是使用.ascx控件文件,类似于.aspx页面,.ascx控件也包括HTML部分和C#代码部分,当这两部分分别保存在不同的文件时,HTML部分使用.ascx文件,而C#代码文件则在.ascx文件名后添加.cs扩展名。
下面的代码(/lib/ctr/PageHeader.ascx)创建了页眉控件。
HTML |
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="PageHeader.ascx.cs" Inherits="lib_ctr_PageHeader" %>
<%@ OutputCache Duration="60" Shared="true" VaryByParam="none" %>
<div class="pageheader">
<a href="/"><h1>ASP.NET项目通用模板</h1></a>
</div>
|
代码中,使用Control指令定义控件参数,并使用OutputCache指令指定缓存参数,其中,Duration参数指定缓存的秒数,请注意,系统资源紧张时会自动释放缓存资源;Shared参数指定是否在所有页面中共享此控件缓存,类似页眉、页脚这样的控件,可以设置为true;VaryByParam参数设置那些参数分别共享,这里设置为none,即不区分参数。
使用.ascx控件时应用注意,对于内容固定的控件,可以使用OutputCache指令设置缓存,而对于需要动态处理的控件,则不应使用缓存,如使用Web控件组合的复合类型的数据处理控件。
下面的代码(/lib/ctr/PageFooter.ascx)创建了页脚控件。
HTML |
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="PageFooter.ascx.cs" Inherits="lib_ctr_PageFooter" %>
<%@ OutputCache Duration="60" Shared="true" VaryByParam="none" %>
<div class="pagefooter">
<p>caohuayu.com©</p>
</div>
|
和C#类控件一样,.ascx控件同样需要注册后使用,如下面的代码,我们在Web.config文件中的pages节点中添加控件注册节点。
XML |
<pages controlRenderingCompatibilityVersion="4.0">
<controls>
<add tagPrefix="acl" namespace="acl.web.ctr" />
<add tagPrefix="acl" tagName="PageHeader" src="/lib/ctr/PageHeader.ascx"/>
<add tagPrefix="acl" tagName="PageFooter" src="/lib/ctr/PageFooter.ascx"/>
</controls>
</pages>
|
这里,同样使用add节点注册.ascx控件,其中的参数包括:
- tagPrefix,设置控件定义前缀。
- tagname,设置控件定义名称。
- src,指定.ascx文件路径。
下面的代码,我们在/Default.aspx页面中测试页眉、页脚控件的使用。
HTML |
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<acl:PageHeader ID="header1" runat="server" />
<form id="form1" runat="server">
<div>
</div>
</form>
<acl:PageFooter ID="footer1" runat="server" />
</body>
</html>
|
页面显示效果如图4。
图4