我们在ASP.NET项目的开发中,需要页面中动态添加和删除多个用户控件。开发环境为Microsoft Visual Studio .NET 2003, 开发语言为C#,并提供源代码下载。 通过一个例子来演示:
我们通过网页来动态创建表和字段时,一次性需要添加多个字段,而字段还有其属性。那么可以通过创建一个用户控件,里面包含多个文本控件或者其他控件来存放一个字段的名称和其多个属性值。然后,在网页中,根据需要创建字段个数的需要,来多次动态加载相等数量的用户控件。 既然能动态加载用户控件,那么加载结束后如果发现某个或几个字段多余,那就需要删除多余的用户控件.在删除时,应该给出提示是否删除。 那么具体操作是:点击"增加"按钮,页面增加一个用户控件,再次点击"增加"按钮又增加一个控件用户。每个用户控件上有一个删除按钮,点击任何用户控件上的“删除”按钮就删除掉该用户控件而不影响其他已存在的用户控件。
我们先提到四个基本概念,他们在动态加载用户控件中需要用到的知识。
什么是用户控件?用户控件就是已经被转换为包含一个或者多个控件的ASP.NET页面. 使用用户控件可以很方便的在多个ASP.NET页面中使用相同的内容和编程逻辑。[1]
什么是PlaceHolder控件?通常不将控件直接添加到页面的Controls集合中。因为我们希望将控件添加到页面的指定的位置。ASP.NET框架中就包含PlaceHolder控件,它用来作为其他控件的容器,我们将控件添加其中,这样我们只需要对PlaceHolder控件定位就可以实现对其他控件的定位。
什么是ViewState?ViewState是.Net中提出的状态保存的一种新途径,它是服务器控件状态保存的基础。通俗的说,就是点击页面的按钮(WebControls.Button类型)提交页面后,页面会被重新加载,那么我们需要保存提交前页面中变量的值,在页面被重新加载后可以使用这些被保存变量的值。在本例中,当我们删除一个控件时,页面会被重新加载,而我们需要删除前用户在其他控件输入的值仍然存在,那么我们就需要ViewState来保存这些值。如果不保存这些值,页面会被重新加载后其他控件输入的值就会被清除,不符合我们的需要。
isPostBack是什么意思?判断页面是否首次被加载,是返回 false, 不是true。通过 isPostBack和ViewState一起就可以在Page_Load中完成页面状态的保存。
完成这个例子的步骤: (1) 新建一个ASP.NET的项目,(有点废话,但是没有项目是无法完成这个例子的)。创建一个后缀名为aspx的页面,我们需要在它里面来动态添加和删除用户控件(假设该页面的名称为TableDataField.aspx)。创建一个后缀名为ascx的用户控件,(假设该用户控件的名称为WebUserControlCls.ascx),我们需要在它里面添加我们需要的多个服务端控件。呵呵,准备工作完成。
(2) 创建一个字段时,在页面TableDataField.aspx和用户控件WebUserControlCls.ascx包含的内容
<1> 页面TableDataField.aspx包含的内容为:
属性名称 | 控件类型 | 用途 |
WPlaceHolder控件 | WebControls.PlaceHolder | 作为其他控件的容器 |
添加按钮 | WebControls.Button | 点击"增加"按钮,页面增加一个用户控件 |
获得属性按钮 | WebControls.Button | 点击"增获得属性"按钮可以获得在用户控件中输入的值 |
表格 | HTML 控件 | 所有的控件放在这个表格里面,5行3列 |
<2>用户控件WebUserControlCls.ascx包含的内容为:
属性名称 | 控件类型 | 用途 |
字段的物理名称 | WebControls.TextBox | 数据库表中的字段名称,一般用英文 |
字段的逻辑名称 | WebControls.TextBox | 对用户显示的字段,作为数据库表中的字段名称的别名 |
字段的属性 | WebControls.DropDownList | 假设字段属性只允许有两个选择:字符类型和数字类型 |
字段的长度 | WebControls.TextBox | 对于字符就是限制长度,是数字就现在其小数位长度 |
删除按钮 | WebControls.Button | 点击“删除”按钮就删除掉该用户控件 |
表格 | HTML 控件 | 所有用户控件放在这个表格里面,1行9列 |
(3)在页面TableDataField.aspx中,说四个要点。 <1>动态增加用户控件
1、获得用户控件 WebUserControlCls webUserCtrl = (WebUserControlCls)Page.LoadControl("WebUserControlCls.ascx"); 2、将用户控件增加到PlaceHolder中 this.plhDataFields.Controls.Add(webUserCtrl); 3、对用户控件的按钮注册动态事件,这里使用事件和代理。关于事件和代理,请参见<<在C#的事件、多播中使用委托>>一文:http://blog.csdn.net/scucj/archive/2006/07/13/915482.aspx
webUserCtrl.btnDelRow.Attributes.Add("onclick", "return confirm('确定要删除该字段吗?');"); webUserCtrl.btnDelRow.Click +=new EventHandler(btnDelRow_Click); 4、对PlaceHolder中的所有用户控件来设置ID属性,将用户控件的ID和被它包含的按钮的ID相同,目的是为了点击用户控件中的"删除"按钮可以通过"删除"按钮ID来获得用户控件ID,然后可以删除该用户控件 setControlsID();
<2>动态删除用户控件 1、创建一个用户控件的实例 WebUserControlCls webUserCtrl = new WebUserControlCls(); 2、通过“删除”按钮的ID来获得用户控件的ID webUserCtrl.ID = btnDel.ID; 3、通过用户控件的ID来删除用户控件 this.plhDataFields.Controls.Remove(this.plhDataFields.FindControl(webUserCtrl.ID)); 4、对PlaceHolder中的剩下所有用户控件来重新设置ID属性 setControlsID();
<3>保持用户控件的状态
IsPostBack表示页面是否首次加载,如果是,则用户控件状态个数为0;如果不是,则要加载用户控件。
if
(
!
IsPostBack )
...
{ //跟踪用户控件状态个数 ViewState[ "DataFieldCount" ] = 0; }
else
...
{ for ( int counter = 0 ; counter < (int)ViewState ["DataFieldCount"]; counter++) ...{ //加载用户控件 AddDataField( counter.ToString() ); } }
<4> 用户控件添加成功后,我们提交这个页面。本例通过点击“获得属性”按钮来获得用户控件中输入的值的。 在这其中,我们通过一个foreach来找到所有的webUserCtrl,然后通过webUserCtrl中提供的属性来获得各个控件输入的值。
if
(ViewState[
"
DataFieldCount
"
]
!=
null
)
...
{ foreach(WebUserControlCls webUserCtrl in this.plhDataFields.Controls) ...{ Response.Write(webUserCtrl.DataFieldName); Response.Write(webUserCtrl.LogicName); Response.Write(webUserCtrl.DataFieldType); Response.Write(webUserCtrl.TypeLength); Response.Write("<br>"); } }
(4)在用户控件WebUserControlCls.ascx中,说三个要点。 <1> 应该在其WebUserControlCls.ascx.cs把“删除”按钮的范围从protected改为public,因为我们需要在 页面TableDataField.aspx中对“删除”按钮添加属性和事件。 这样: protected System.Web.UI.WebControls.Button btnDelRow 应该变为:public System.Web.UI.WebControls.Button btnDelRow 注意,在用户控件WebUserControlCls.ascx移动某个控件时,“删除”按钮的范围会从已经修改的public变回为protected。那么就需要再次将protected改为public。
<2> 在用户控件WebUserControlCls.ascx中为各个控件定义属性,以便页面TableDataField.aspx中可以方便获取控件中输入的值。 比如: 物理名称,只读属性
public
string
DataFieldName
...
{ get ...{ return txtDataFieldName.Text.Trim(); } }
逻辑名称,只读属性
public
string
LogicName
...
{ get ...{ return txtLogicName.Text.Trim(); } }
呵呵,整个思路就是这样。由于CSDN的blog不能上传文件,我下面给出 TableDataField.aspx 和 WebUserControlCls.ascx 两个文件的四个部分的源代码。或者给我发邮件:scucj@126.com。
<1> TableDataField.aspx
<%
...
@ Page language="c#" Codebehind="TableDataField.aspx.cs" AutoEventWireup="false" Inherits="ScuHR.TableSet.TableDataField"
%>
<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
>
<
HTML
>
<
HEAD
>
<
title
>
TableDataFiled
</
title
>
<
meta
name
="GENERATOR"
Content
="Microsoft Visual Studio .NET 7.1"
>
<
meta
name
="CODE_LANGUAGE"
Content
="C#"
>
<
meta
name
="vs_defaultClientScript"
content
="JavaScript"
>
<
meta
name
="vs_targetSchema"
content
="http://schemas.microsoft.com/intellisense/ie5"
>
</
HEAD
>
<
body
MS_POSITIONING
="GridLayout"
>
<
form
id
="Form1"
method
="post"
runat
="server"
>
<
TABLE
id
="Table1"
style
="Z-INDEX: 101; LEFT: 56px; WIDTH: 680px; POSITION: absolute; TOP: 32px; HEIGHT: 75px"
cellSpacing
="1"
cellPadding
="1"
width
="680"
border
="1"
>
<
TR
>
<
TD
style
="WIDTH: 21px"
><
FONT
face
="宋体"
></
FONT
></
TD
>
<
TD
style
="WIDTH: 621px"
></
TD
>
<
TD
></
TD
>
</
TR
>
<
TR
>
<
TD
style
="WIDTH: 21px"
></
TD
>
<
TD
style
="WIDTH: 621px"
>
<
asp:PlaceHolder
id
="plhDataFields"
runat
="server"
></
asp:PlaceHolder
></
TD
>
<
TD
></
TD
>
</
TR
>
<
TR
>
<
TD
style
="WIDTH: 21px"
></
TD
>
<
TD
style
="WIDTH: 621px"
></
TD
>
<
TD
></
TD
>
</
TR
>
<
TR
>
<
TD
style
="WIDTH: 21px"
></
TD
>
<
TD
style
="WIDTH: 621px"
>
<
asp:Button
id
="btnAadd"
runat
="server"
Text
="增加"
></
asp:Button
></
TD
>
<
TD
></
TD
>
</
TR
>
<
TR
>
<
TD
style
="WIDTH: 21px"
></
TD
>
<
TD
style
="WIDTH: 621px"
><
FONT
face
="宋体"
>
<
asp:Button
id
="btnGetValue"
runat
="server"
Text
="获得属性值"
></
asp:Button
></
FONT
></
TD
>
<
TD
></
TD
>
</
TR
>
</
TABLE
>
</
form
>
</
body
>
</
HTML
>
<2>TableDataField.aspx .cs
using
System;
using
System.Collections;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Web;
using
System.Web.SessionState;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Web.UI.HtmlControls;
namespace
ScuHR.TableSet
...
{ /**//// <summary> /// TableDataFiled 的摘要说明。 /// </summary> /// public class TableDataField : System.Web.UI.Page ...{ protected System.Web.UI.WebControls.Button btnAadd; protected System.Web.UI.WebControls.PlaceHolder plhDataFields; protected System.Web.UI.WebControls.Button btnGetValue; private void Page_Load(object sender, System.EventArgs e) ...{ if (! IsPostBack ) ...{ //跟踪用户控件状态值 ViewState[ "DataFieldCount" ] = 0; } else ...{ for ( int counter = 0 ; counter < (int)ViewState ["DataFieldCount"]; counter++) ...{ AddDataField( counter.ToString() ); } } } Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码 override protected void OnInit(EventArgs e) ...{ // // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。 // InitializeComponent(); base.OnInit(e); } /**//// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() ...{ this.btnAadd.Click += new System.EventHandler(this.btnAadd_Click); this.btnGetValue.Click += new System.EventHandler(this.btnGetValue_Click); this.Load += new System.EventHandler(this.Page_Load); } #endregion private void btnAadd_Click(object sender, System.EventArgs e) ...{ ViewState[ "DataFieldCount" ] = (int)ViewState[ "DataFieldCount" ] + 1; AddDataField( ViewState[ "DataFieldCount" ].ToString() ); } /**//// <summary> /// 通过用户控件来增加动态控件 /// </summary> /// <param name="strFieldNum"></param> private void AddDataField(string strFieldNum) ...{ //获得用户控件 WebUserControlCls webUserCtrl = (WebUserControlCls)Page.LoadControl("WebUserControlCls.ascx"); //将用户控件增加到PlaceHolder中 this.plhDataFields.Controls.Add(webUserCtrl); //对用户控件的按钮注册动态事件 webUserCtrl.btnDelRow.Attributes.Add("onclick", "return confirm('确定要删除该字段吗?');"); webUserCtrl.btnDelRow.Click +=new EventHandler(btnDelRow_Click); //对PlaceHolder中的所有用户控件来设置ID属性,目的是为了点击用户控件中的"删除"按钮删除该用户控件 setControlsID(); } /**//// <summary> /// 删除所选择的那行用户控件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnDelRow_Click(object sender, System.EventArgs e) ...{ // ViewState[ "DataFieldCount" ] = (int)ViewState[ "DataFieldCount" ] - 1; Button btnDel = (Button)sender; WebUserControlCls webUserCtrl = new WebUserControlCls(); webUserCtrl.ID = btnDel.ID; this.plhDataFields.Controls.Remove(this.plhDataFields.FindControl(webUserCtrl.ID)); //对PlaceHolder中的所有用户控件来重新设置ID属性 setControlsID(); } /**//// <summary> /// 获得所有用户控件中的值 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnGetValue_Click(object sender, System.EventArgs e) ...{ if(ViewState["DataFieldCount"]!=null) ...{ foreach(WebUserControlCls webUserCtrl in this.plhDataFields.Controls) ...{ Response.Write(webUserCtrl.DataFieldName); Response.Write(webUserCtrl.LogicName); Response.Write(webUserCtrl.DataFieldType); Response.Write(webUserCtrl.TypeLength); Response.Write("<br>"); } } } /**//// <summary> /// 对PlaceHolder中的所有用户控件来设置ID属性 /// </summary> private void setControlsID() ...{ int i=1; //将用户控件与其按钮设置为相同的ID foreach(WebUserControlCls wc in this.plhDataFields.Controls) ...{ wc.ID = i.ToString(); wc.btnDelRow.ID =i.ToString(); i++; } } } }
<3>WebUserControlCls.ascx
<%
...
@ Control Language="c#" AutoEventWireup="false" codebehind="WebUserControlCls.ascx.cs" Inherits="ScuHR.TableSet.WebUserControlCls" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"
%>
<
TABLE
id
="Table1"
style
="WIDTH: 746px; HEIGHT: 30px"
cellSpacing
="0"
cellPadding
="0"
width
="746"
border
="1"
>
<
TR
>
<
TD
>
物理名称:
</
TD
>
<
TD
><
FONT
face
="宋体"
><
asp:textbox
id
="txtDataFieldName"
runat
="server"
CssClass
="textbox"
MaxLength
="20"
Width
="117"
Height
="24"
></
asp:textbox
></
FONT
></
TD
>
<
TD
><
FONT
face
="宋体"
>
逻辑名称
</
FONT
></
TD
>
<
TD
style
="WIDTH: 119px"
><
asp:textbox
id
="txtLogicName"
runat
="server"
CssClass
="textbox"
MaxLength
="20"
Width
="117px"
Height
="24px"
></
asp:textbox
></
TD
>
<
TD
style
="WIDTH: 75px"
><
FONT
face
="宋体"
>
字段属性:
</
FONT
></
TD
>
<
TD
style
="WIDTH: 87px"
><
FONT
face
="宋体"
><
asp:dropdownlist
id
="dropDataType"
runat
="server"
>
<
asp:ListItem
Value
="0"
>
字符类型
</
asp:ListItem
>
<
asp:ListItem
Value
="1"
>
数字类型
</
asp:ListItem
>
</
asp:dropdownlist
></
FONT
></
TD
>
<
TD
><
FONT
face
="宋体"
>
属性长度:
</
FONT
></
TD
>
<
TD
style
="WIDTH: 40px"
><
FONT
face
="宋体"
><
asp:textbox
id
="txtTypeLength"
runat
="server"
Width
="36px"
Height
="23px"
>
30
</
asp:textbox
></
FONT
></
TD
>
<
TD
><
FONT
face
="宋体"
><
FONT
face
="宋体"
><
asp:button
id
="btnDelRow"
Text
="删除"
runat
="server"
></
asp:button
></
FONT
></
FONT
></
TD
>
</
TR
>
</
TABLE
>
<4>WebUserControlCls.ascx .cs
namespace
ScuHR.TableSet
...
{ using System; using System.Data; using System.Drawing; using System.Web; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Text.RegularExpressions; //使用正则表达式 /**//// <summary> /// WebUserControl 的摘要说明。 /// </summary> public class WebUserControlCls : System.Web.UI.UserControl ...{ public System.Web.UI.WebControls.Button btnDelRow;//注意,应该是public protected System.Web.UI.WebControls.DropDownList dropDataType; protected System.Web.UI.WebControls.TextBox txtLogicName; protected System.Web.UI.WebControls.TextBox txtDataFieldName; protected System.Web.UI.WebControls.TextBox txtTypeLength; private void Page_Load(object sender, System.EventArgs e) ...{ // 在此处放置用户代码以初始化页面 } /**//// <summary> /// 物理名称,只读属性 /// </summary> public string DataFieldName ...{ get ...{ return txtDataFieldName.Text.Trim(); } } /**//// <summary> /// 逻辑名称,只读属性 /// </summary> public string LogicName ...{ get ...{ return txtLogicName.Text.Trim(); } } /**//// <summary> /// 数据类型,0表示是字符串类型,1表示是数字类型 /// </summary> public int DataFieldType ...{ get ...{ return Int32.Parse(dropDataType.SelectedValue); } } public int TypeLength ...{ get ...{ //使用正则表达式判断字符串是否为数字 //是数字则返回该数字的值 //不是则返回-1 Regex r = new Regex(@"^d+$"); if(r.IsMatch(txtTypeLength.Text.Trim())) ...{ return Int32.Parse(txtTypeLength.Text); } else ...{ return -1; } } } Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码 override protected void OnInit(EventArgs e) ...{ // // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。 // InitializeComponent(); base.OnInit(e); } /**//// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器 /// 修改此方法的内容。 /// </summary> private void InitializeComponent() ...{ this.Load += new System.EventHandler(this.Page_Load); } #endregion } }