一、母版页
在制作页面的过程中, 多个页面往往具有相同的页面Header和页面Footer, 多个页面只是在中间部分有变化. 那么我们完全可以避免在每个页面中都写一遍页头和页尾的代码, 这种技术就是母版页, 其实用户控件也可以实现相同的功能. 我们可以使用母版页把页面中相似的部分提取出来, 而把各面之间不同的部分放在一个称为ContentPlaceHolder的标签对中, 之后我们创建页面的时候, 可以勾选”选择母版页”, 进而创建那些框架类似的页面, 这时即使我们不写一行代码, 刚创建的页面也包含有母版页的内容. 剩下的工作, 就是在新建页面的Content标签中写内容就可以了, 注意: 母版页中是可以规定多个ContentPlaceHolder块的. 母版页及页面的搭建都比较简单, 这里不做说明.
关于母版页的两个小技巧:
1. 修改内容也的标题: 只需要在内容也的页面指令处, 添加属性Title = “Page Title”即可, 这样Page Title就会像是在IE的标题栏.
2. 内容页中修改母版页中的数据:
a.) 在母版页中添加控件(除ContentPlaceHolder标签对外的任何位置). 常用来放置登录后的用户信息.
b.) 在母版页的后台cs文件中, 制作公有属性将刚添加的控件暴露出来.
c.) 在内容页中添加个页面指令, 该页面指令的作用, 是使内容页识别我们在母版页中添加的自定义信息. 指令如下:
<%@ MasterType VirtualPath=“~/MagerPage.master” %>
d.) 在内容页中用母版页对象this.Master获取母版页中添加的控件的引用(通过自定的属性来获取), 进而达到通过内容页操作母版页的目的.
二、用户控件(Web User Control)
用户控件是微软在ASP.NET中定义的一个类, 在VS编辑器模板中的名称是Web User Control, 后缀名为ascx. 用户控件和母版页的功能类似(在没有母版页这项技术的时候, 类似页面Header和页面Footer的工作就是通过用户控件实现的).
用户控件(Web User Control)和自定义控件(第三方控件, Custom Control)区别:
容易引起混淆的地方是用户控件和自定义控件(第三方控件), 这两者最根本的区别是: 用户控件是微软定义的类, ASP.NET甚至为我们定义好了模板, 用户控件的作用类似于C#中的常量, 我们将页面上多次用到的重复部分做成用户控件, 但自定义控件是我们程序员自己定义的, 我们通过定义一个类继承自某个控件类, 来完善控件的功能或将多个控件组成复合控件. 其他的区别还有: 用户控件只能放在解决方案管理器中, 我们在解决方案管理器的项目中拖动用户控件到页面将会自动生成事先定义的重复代码, 而自定义控件是放在工具箱中的, 与TextBox、Button等控件属于同一级别.
前者解决了重复代码的问题(生成内容相对固定), 后者则创建了一个新的控件(可根据参数调整). 就好比: 建筑行业中, 用用户控件就类似于砖头和水泥组合成的墙, 在我的建筑工程中, 我可以说: “这儿来一堵墙, 那儿再来一堵墙”来重复砖头和水泥的组合; 自定义控件就类似于建好的某个房间, 是个成熟的东西. 我可以说:”这儿来个房间, 房间大小是15*15米, 形状是椭圆的”等等.
创建用户控件:
用户控件的制作相对简单, 你完全可以用写页面的方式来写用户控件. 当我们用VS编辑器在网站项目中创建一个扩展名为.ascx的用户控件时, 可以看到用户控件派生子System.Web.UI.UserControl, 注意: 拖动用户控件时, 只能在设计视图中. 当拖放操作完成后, 会在页面中生成<%@ Register …%>指令, 其中src表示用户控件路径; tagname是用户控件的类名, 同时也是相应标签的名字, 如: <ucl: tagname…>; tagprefix表示控件的前缀, 如: ucl. 注意工具箱中的控件前缀为asp.
1.) 初始化用户控件: 初始化用户控件的工作不要放在构造函数中, 最好放在页面对象的Page_Init事件中.
2.) 可以再用户控件中定义属性、方法、事件(经常将用户控件中的显示控件以公有属性的形式暴露出来, 在前台页面使用用户控件的id获得公有属性).
3.) 可以再用户控件上提供局部缓存管理.
通过如下指令, 设置用户控件的缓存: 在用户控件的文件(*.ascx)中, 添加页面指令:
<%@ OutputCache Duration=”10000” VaryByParam=”page;categoryID” %>
其中, duration表示以秒为单位的缓存时间; VaryByParam表示在 请求参数 中的page和categoryID发生变化时需要单独的缓存处理, 也就是说当页面的请求参数的值发生变化时, 浏览器会自动发送一个新的请求并重新缓存页面. 注意: <OutputCache指令中VaryByParam属性的值以分号分隔, 而相应页面的URL中就是用&拼接的名值对.
Location属性用来设置缓存的位置, 可以再本地缓存也可以在服务器中缓存, 但是由于用户控件就相当于部分页面, 因此缓存在本地没有意义, 用户控件或部分页面的缓存通常放在服务器上.
再*.ascx.cs中使用this.CachePolicy.Dependency 设置缓存依赖.
三、自定义控件(第三方控件)
控件是ASP.NET技术的核心, 微软在ASP.NET中针对常见的Web应用开发了很多基本控件, 开发人员可以使用这些控件完成模板页的制作(*.aspx页面). 但是在实际的开发过程中, 可能有些功能无法通过这些基本控件实现, 也有可能需要对基本控件做些扩充, 这时候就需要用到自定义控件(Custom Control), 也有公司喜欢称为第三方控件.
自定义控件和label、textbox控件一样, 它就是一个控件, 需要放在”工具箱(ToolBox)”中. 前面说过, 与用户控件不同, 用户控件是用来将项目中多次用到的代码封装起来, 当我们拖放一个用户控件时, 机器将为我们生成那些重复的代码; 而自定义控件将会有自己的功能、属性及表现形式.
创建自定义控件:
创建自定义控件有三种方式: \
第一种是从Control类派生, 如果自定义控件不需要在网页上生成内容, 仅在服务器端参与请求处理过程, 就可以从Control类派生.
第二种是从WebControl类派生, 如果自定义控件需要在网页上显示出来, 就可以从WebControl类派生.
第三种是从CompositeControl类派生, 这是工作中比较常用的情况. 该种情况下, 自定义控件不是用Control和WebControl直接派生, 而是先找到功能相近的ASP.NET控件或某个复合控件, 然后自定义控件派生自找到的控件, 并在其基础上做些扩展.
自定义控件就是一个类, 在开发过程中表现为类库项目. 其中, Windows Form窗体程序中的自定义控件在VS08中称为CustomControl(派生自Control类), 而在Web Form程序中, 自定义控件称为Server Control(派生自WebControl类), 他们派生自不同的类. 参见下图, 展示了自定义控件所涉及的相关内容:

在实际的开发过程中, 前两种的从无到有的创建控件的方式, 用的很少, 其中最主要的是重写Render方法. 这些常用的控件微软都已经为我们做好了实现, 我们更常见的方式就是从CompositeControl类派生, 使用多个基本控件组合出一个复合控件来完成复杂的功能.
我们通过示例来说明自定义控件的制作过程, 自定义登录控件是面试常考的:
注意: Asp.Net提供了一个名称为Login的登录控件, 为了避免可能的混乱, 在我们创建WebForm或自定义控件时, 避免使用Login做名称.
制作自定义控件的过程:
1. 自定义类派生自System.Web.UI.WebControls.CompositeControl, 并在类中创建自定义控件所需要的基本控件类型的变量, 而且需要对用来显示内容的控件 或者针对显示内容控件的 有关显示的属性 而制作公有属性.
注意: 在制作属性的过程中, 需要在Get和Set访问器中使用EnsureChildControls()方法强制绘制基本控件.
2. 必须在自定义控件的类中重写CreateChildControl()方法, 该方法负责创建基本控件(或者说子控件), 并对其进行初始化设置, 相当于自定义控件类中的构造函数. 最后, 别忘了把初始化完毕的基本控件通过this.Controls.Add()方法添加到当前的自定义控件中.
3. 必须在自定义控件的类中重写RenderContents()方法, 该方法用来布局. 可以使用基本控件上的RenderControl()方法绘制基本控件. 注意: 因为在该方法中要生成内容, 所以应该先设置布局元素的属性(Attribute), 后写生成的标签.
4. 必须在自定义控件的类中重写一个布局的属性(override HtmlTextWriterTag TagKey). 该属性用于设置布局的方式, table还是div.
5. 添加到工具箱中, 方法: 右键工具箱, 选择”选择项”, . 顺便提下, 我们可以为自定义控件添加强名称后放入GAC中, 这样每次只需要拖动即可.
6. 为自定义控件添加事件: 在自定义控件的类中, 直接定义事件即可(用EventHandler委托), 定义好事件后, 需要在按钮的Click事件中注册事件处理方法. 为了能够实现双击自定义控件, 自动在后台.cs文件中添加事件处理器模板, 自定义控件的类中的按钮的事件处理器, 需要先判断事件不为空, 然后通过定义的事件名调用方法即可. 注意: 关于事件名方法的参数, 如果希望点击整个自定义控件执行某个事件处理器, 那么事件名方法的参数为this和e, this表示的是自定义控件; 如果希望点击自定义控件中的某个按钮后激发某个事件处理器, 那么事件名方法的参数为sender和e, sender表示点击的按钮. 一般情况下, 用this作参数时, 在后台.cs文件中, 可以通过sender取得自定义控件的所有属性, 因此可以将自定义控件的主要事件用个this作参数, 而用sender作参数时, 在后台.cs文件中, 可以通过自定义控件的ID获得自定义控件的所有属性.
备注:
1. 可以给自定义控件添加图标, 做法很简单, 只要在自定义控件的类的前面加上标签:
[System.Drawing.ToolboxBitmap(typeof(自定义控件名), "命名空间.图片名(与类名相同).bmp")] 即可.
设置图标有3个注意事项: a. 一定要是16*16的bmp图片 b. 图片名称一定要与类名相同且需要加命名控件 c.右键点击图片, 在BuildAction中选中”嵌入资源”.
2. 自定义控件视图状态的管理(对于那些本身不是控件类或没有派生自Controls的类), 需要实现System.Web.UI.IStateManager接口. 之后再使用控件的cs文件中, 通过重写SaveViewState()和LoadViewState()这两个方法, 实现自定义控件的视图状态管理.
//示例代码: 一共4页面, 包括母版页、default.aspx(自定义控件)、Register(用户控件)、UserManagement(母版页)
//MasterPage.master
 代码
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www./TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www./1999/xhtml"> <head runat="server"> <title>MasterPage</title> </head> <body style="text-align:center;"> <form id="frm_master" runat="server" style="width:800px;"> <div id="header"> <table> <tr><td><a href="Default.aspx">首页(自定义控件或第三方控件)</a> </td><td><a href="Register.aspx">注册用户(用户控件)</a> </td><td><a href="UserManagement.aspx">用户管理(内容页修改母版页)</a> </td></tr> </table> </div> <div> <asp:Label ID="lbl_master" runat="server" Text="母版页的文本框信息"></asp:Label> <asp:TextBox ID="txt_master" runat="server" Text="默认为母版页的值"></asp:TextBox><hr /><br /> <asp:ContentPlaceHolder id="cpHolder" runat="server"> </asp:ContentPlaceHolder> </div> <br style="clear:both"/><%--清除样式--%> <div> <hr /> <br /> <table><tr><td>XXX公司</td></tr><tr><td>版权所有2010-2011</td></tr></table></div> </form> </body> </html>
//MasterPage.master.cs
 代码
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Xml.Linq;
public partial class MasterPage : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { }
//暴露母版页中的信息, 我们只需要在内容页中拿到母版页控件的引用即可, 不需要Set访问器 public Label MasterLabel { get { return this.lbl_master; } }
public TextBox MasterTextBox { get { return this.txt_master; } } }
//default.aspx -- 自定义控件
 代码
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %> <%@ OutputCache Duration="1" VaryByParam="count" %>
<%@ Register Assembly="CustomControl" Namespace="CustomControl" TagPrefix="cc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="cpHolder" Runat="Server"> <cc1:LoginControl ID="LoginControl1" runat="server" onloginclick="LoginControl1_LoginClick" onregisterclick="LoginControl1_RegisterClick" /> </asp:Content>
//default.aspx.cs
 代码
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq;
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void LoginControl1_LoginClick(object sender, EventArgs e) { if (this.LoginControl1.UserName == "zhangsan" && this.LoginControl1.Password == "zhangsan" && this.LoginControl1.InputCode.ToUpper() == this.Session["dycode"].ToString()) { this.Response.Redirect("UserManagement.aspx"); } else { this.Page.ClientScript.RegisterStartupScript(this.GetType(), "CScript", "<script>alert('验证失败! ');</script>"); } } protected void LoginControl1_RegisterClick(object sender, EventArgs e) { this.Response.Redirect("Register.aspx"); }
//在控件中通过重写基类的SavaViewState()和LoadViewState()方法, 来实现自定义控件的视图状态的管理 protected override object SaveViewState() { object x = base.SaveViewState(); object y = this.LoginControl1.SaveViewState(); return new System.Web.UI.Pair(x, y); } protected override void LoadViewState(object savedState) { if (savedState != null) { System.Web.UI.Pair pair = savedState as System.Web.UI.Pair; base.LoadViewState(pair.First); this.LoginControl1.LoadViewState(pair.Second); } } }
//dynamiccode.ashx
 代码
<%@ WebHandler Language="C#" Class="dynamiccode" %>
using System; using System.Web;
public class dynamiccode : IHttpHandler,System.Web.SessionState.IRequiresSessionState { private static readonly string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; private Random ran = new Random(); public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "image/jpeg"; context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(100, 25);//Bitmap和Metafile是抽象类System.Drawing.Image的派生类, 图片宽度从0-79, 代表点数 using(System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage(bmp)) { graphic.Clear(System.Drawing.Color.LightGreen);//类似橡皮擦, 用背静填充 for (int i = 0; i < 20; i++) { System.Drawing.Point startpt = new System.Drawing.Point(ran.Next(0, 100), ran.Next(0, 25));//ran.Next取左不取右, //System.DrawLine需要的两个对象 System.Drawing.Point endpt = new System.Drawing.Point(ran.Next(0, 100), ran.Next(0, 25));//System.DrawLine需要的两个对象
//要画直线, 需要两个System.Drawing.Point类型的对象和1个画笔对象 graphic.DrawLine(System.Drawing.Pens.DarkCyan, startpt, endpt); }
//画图片的外框 System.Drawing.Rectangle rt = new System.Drawing.Rectangle(0,0,99,24);//定义矩形框 graphic.DrawRectangle(System.Drawing.Pens.BlueViolet, rt); //画验证码 string strrandom = ""; for (int i = 0; i < 5; i++) { strrandom += charset[ran.Next(0,charset.Length)]; } //保存到Session中 context.Session["dycode"] = strrandom; //定义字体 System.Drawing.Font ft = new System.Drawing.Font("黑体",20,System.Drawing.FontStyle.Bold); //定义需要被绘制的字符串的格式 System.Drawing.StringFormat sf = new System.Drawing.StringFormat(); sf.Alignment = System.Drawing.StringAlignment.Center;//垂直居中 sf.LineAlignment = System.Drawing.StringAlignment.Center;//水平居中
graphic.DrawString(strrandom, ft, System.Drawing.Brushes.DarkOliveGreen, rt, sf); }
//通过context.Response.OutputStream流, 将位图文件写入到浏览器 bmp.Save(context.Response.OutputStream,System.Drawing.Imaging.ImageFormat.Jpeg); } public bool IsReusable { get { return false; } }
}
// Register.aspx -- 用户控件
 代码
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Register.aspx.cs" Inherits="Register" Title="注册用户(用户控件)" %>
<%@ Register src="RegisterUser.ascx" tagname="RegisterUser" tagprefix="uc1" %>
<%--需要在设计视图下, 拖动用户控件--%> <asp:Content ID="Cont_Register" ContentPlaceHolderID="cpHolder" Runat="Server"> <uc1:RegisterUser ID="RegisterUser1" runat="server" /> </asp:Content>
//Register.aspx.cs
 代码
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq;
public partial class Register : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //使用用户控件中定义的公共属性 this.RegisterUser1.RegInfo.Text = "左边为必填项! "; } }
//RegisterUser.ascx
 代码
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="RegisterUser.ascx.cs" Inherits="RegisterUser" %>
<div style="float:left"> <table border="0" cellpadding="0" cellspacing="0"> <tr> <td>用户名: </td> <td style="width:150px"> <asp:TextBox ID="txt_name" runat="server"></asp:TextBox></td> <td rowspan="4"> <asp:TextBox ID="txt_infos" runat="server" BorderStyle="None" Height="100px" TextMode="MultiLine" ></asp:TextBox></td> </tr> <tr> <td>密 码: </td> <td> <asp:TextBox ID="txt_pass" runat="server"></asp:TextBox></td> </tr> <tr> <td>重复密码:</td> <td> <asp:TextBox ID="txt_confirm" runat="server"></asp:TextBox></td> </tr> <tr> <td>邮 箱: </td> <td> <asp:TextBox ID="txt_email" runat="server"></asp:TextBox></td> </tr> <tr> <td></td> <td> <asp:Button ID="btn_submit" runat="server" Text="注册" onclick="btn_submit_Click" /></td> </tr> </table> </div> <div style="margin-left:50px;float:left" > <asp:Label ID="lbl_userlist" style="margin-left:50px;" runat="server" Text="已有用户:"></asp:Label><br /> <div style="width:60%"> <asp:Repeater ID="rpt_userlist" runat="server" > <HeaderTemplate> <table cellspacing="0" cellpadding="0" border="0" style="width:100%;border-collapse:collapse"> <tr><th scope="col">用户名</th><th scope="row">邮箱</th></tr> </HeaderTemplate> <ItemTemplate> <tr align="center"> <td><%# DataBinder.Eval(Container.DataItem,"Key") %></td> <td><%# Eval("Value") %></td> </tr> </ItemTemplate> <FooterTemplate> </table> </FooterTemplate> </asp:Repeater> </div> </div><br />
//RegisterUser.ascx.cs
 代码
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq;
public partial class RegisterUser : System.Web.UI.UserControl { //使用Dictionary提供些模拟数据, 供Datelist使用 System.Collections.Generic.Dictionary<string, string> users;
//初始化 protected void Page_Init(object sender, EventArgs e) { this.txt_name.Text = "用户名只能为字母!"; users = new System.Collections.Generic.Dictionary<string, string>(); users.Add("张三", "zhangsan@zs.com"); users.Add("李四", "lisi@ls.com"); users.Add("王五", "wanwu@ww.com"); }
protected void Page_Load(object sender, EventArgs e) { BindData(); }
//定义属性, 通常将用户控件中的显示控件的属性暴露出来(用来在页面中赋值, 只要用get获得其引用即可) public TextBox RegInfo { get { return this.txt_infos; } }
//定义事件处理方法 protected void btn_submit_Click(object sender, EventArgs e) { string msg = string.Empty; if (string.IsNullOrEmpty(this.txt_name.Text) || string.IsNullOrEmpty(this.txt_pass.Text) || string.IsNullOrEmpty(this.txt_email.Text)) { if (string.IsNullOrEmpty(this.txt_name.Text)) { msg += "用户名不能为空! "; msg += Environment.NewLine; } if (string.IsNullOrEmpty(this.txt_pass.Text)) { msg += "密码不能为空! "; msg += Environment.NewLine; } if (string.IsNullOrEmpty(this.txt_email.Text)) { msg += "邮箱不能为空! "; msg += Environment.NewLine; } } else { msg = "您输入的注册信息如下: "; if (!string.IsNullOrEmpty(this.txt_name.Text)) { msg += this.txt_name.Text; msg += Environment.NewLine; } if (!string.IsNullOrEmpty(this.txt_pass.Text)) { msg += this.txt_pass.Text; msg += Environment.NewLine; } if (!string.IsNullOrEmpty(this.txt_email.Text)) { msg += this.txt_email.Text; msg += Environment.NewLine; } } this.txt_infos.Text = msg; }
private void BindData() { this.rpt_userlist.DataSource = this.users; this.rpt_userlist.DataBind(); } }
//UserManagerment.aspx --- 母版页
 代码
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="UserManagement.aspx.cs" Inherits="UserManagement" Title="用户管理" %> <%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="cont_usermanage" ContentPlaceHolderID="cpHolder" Runat="Server"> <label for="txt_name">用户名</label> <asp:TextBox ID="txt_name" runat="server"></asp:TextBox> <br /> <label for="txt_tel">联系电话</label> <asp:TextBox ID="txt_tel" runat="server"></asp:TextBox> <br /> <asp:Button ID="btn_submit" runat="server" Text="提交" onclick="btn_submit_Click" /> </asp:Content>
//UserManagerment.aspx.cs
 代码
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq;
public partial class UserManagement : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { }
protected void btn_submit_Click(object sender, EventArgs e) { //在内容页中, 通过母版页对象Master获得已暴露的控件的引用(通过属性获得) this.Master.MasterLabel.Text = this.txt_name.Text; this.Master.MasterTextBox.Text = this.txt_tel.Text; } }
//自定义控件的项目, 省略了LoginControl.bmp
//LoginControl.cs
|