ASP.NET控件编程
用户控件
ASP.NET中创建用户控件
1、什么是用户控件?
• ASP.NET中的组件
• 是封装到可重用控件中的Web表单。它们用来保存网站中许多页面所需要的重用代码块。
2、用户控件的优点
• 用户控件适用于:
–页面上的重复元素,比如标题、菜单、登录控件等。
–将重复元素封装到用户控件中,减少每页的代码量
3 、创建用户控件-重复元素
§ VS.NET中用户控件的创建
§ 新建ASP.NET Web应用程序
§ 在“项目”菜单中选择“添加Web用户控件”
§ 创建界面元素
代码分析(main.aspx)
• <%@Register TagPrefix= "UserControl" TagName="Header" Src="Header.ascx"%>
– TagPrefix:控件组的通称
– TagName:这个控件的名称
– 使用:UserControl:Header
VS.NET中用户控件的使用
• 设计方式:
–把*.ascx文件拖动到*.aspx页面上,即可添加用户控件
• 编码方式
– myTBPlus=(TextBoxPlus)LoadControl("TextBoxPlus.ascx");
– PlaceHolder1.Controls.Add(myTBPlus);
【实例代码】
下面代码主要作用是动态修改用户控件的属性:通过修改属性bgcolor="<%=strColor%>来修改背景颜色
header.ascx:
<%@ Control Language="c#" Inherits="FirstUserControl.header" CodeFile="header.ascx.cs" %>
<table bgcolor="<%=strColor%>" width="100%" cellpadding="10" cellspacing="0">
<tr>
<td width="364" style="WIDTH: 364px">
<img src="logo.gif" align="left">
</td>
<td width="498" style="WIDTH: 498px">
<font face="verdana,arial" size="4" color="#0000cc">欢迎光临浏览本网站!</font>
</td>
<td width="300" style="WIDTH: 300px">
<font face="verdana,宋体" size="2" color="#ff0099">MSDNWebcast中文网站</font>
</td>
</tr>
</table>
Header.ascx.cs
public partial class header : System.Web.UI.UserControl
{
public string strColor = "Red";
protected void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
}
}
main.aspx.cs
protected void btnApply_Click(object sender, System.EventArgs e)
{
lbColor.Text = ddlColor.SelectedItem.Text;
//改变用户控件背景色
Header1.strColor = ddlColor.SelectedItem.Text;
}
下面代码主要作用是采用编码方式在网页中动态添加用户控件:
mikecatctr.ascx:
<%@ Control Language="c#" Inherits="ASPNETUseCom.UseControl.MikeCatCtr" CodeFile="MikeCatCtr.ascx.cs" %>
<span style="FONT: 12pt verdana">Category:
<%=Category%>
</span>
<ASP:DataList id="MyDataList" BorderWidth="0" RepeatColumns="2" runat="server">
<ItemTemplate>
<table cellpadding="10" style="font: 10pt verdana">
<tr>
<td valign="top">
<b>Title: </b>
<%# DataBinder.Eval(Container.DataItem, "title") %>
<br>
<b>Category: </b>
<%# DataBinder.Eval(Container.DataItem, "type") %>
<br>
<b>Publisher ID: </b>
<%# DataBinder.Eval(Container.DataItem, "pub_id") %>
<br>
<b>Price: </b>
<%# DataBinder.Eval(Container.DataItem, "price", "$ {0}") %>
</td>
</tr>
</table>
</ItemTemplate>
</ASP:DataList>
MikeCatCtr.ascx.cs:
private String _category = "";
public String Category
{
get
{
return _category;
}
set
{
_category = value;
SqlConnection myConnection = new SqlConnection("server=(local);database=pubs;uid=sa;pwd=*******");
SqlDataAdapter myCommand = new SqlDataAdapter("select * from Titles where type='" + _category + "'", myConnection);
DataSet ds = new DataSet();
myCommand.Fill(ds, "Titles");
MyDataList.DataSource = ds.Tables["Titles"].DefaultView;
MyDataList.DataBind();
}
}
main.aspx
<%@ Reference Control="~/usecontrol/mikecatctr.ascx" %>
<%@ Register TagPrefix="uc1" TagName="MikeCatCtr" Src="MikeCatCtr.ascx" %>
<!--以下代码省略-->
main.aspx.cs
protected void Page_Load(object sender, System.EventArgs e)
{
Page.Controls.Add(new HtmlGenericControl("hr"));
Control c1 = LoadControl("MikeCatCtr.ascx");
((MikeCatCtr)c1).Category = "business";
Page.Controls.Add(c1);
Page.Controls.Add(new HtmlGenericControl("hr"));
Control c2 = LoadControl("MikeCatCtr.ascx");
((MikeCatCtr)c2).Category = "trad_cook";
Page.Controls.Add(c2);
Page.Controls.Add(new HtmlGenericControl("hr"));
Control c3 = LoadControl("MikeCatCtr.ascx");
((MikeCatCtr)c3).Category = "mod_cook";
Page.Controls.Add(c3);
}
用户控件小结
• 用户控件使开发人员能够使用编写Web 窗体页的相同编程技巧轻松地定义自定义控件。
• 作为约定,用.ascx 文件扩展名指示这样的控件。
• 用户控件通过Register 指令包括在另一Web窗体页中,该指令指定TagPrefix、TagName 和Srclocation 。
• 注册了用户控件后,可以像普通的服务器控件那样将用户控件标记放置在Web 窗体页中(包括runat=“server” 属性)。
• 在包含Web 窗体页中将用户控件的公共字段、属性和方法提升为该控件的公共属性
(标记属性)和方法。
• 用户控件参与每个请求的整个执行生存期,并且可以处理自己的事件,封装来自包含Web 窗体页的一些页逻辑。
• 可以使用System.Web.UI.Page 类的LoadControl 方法以编程方式创建用户控件。用户控件的类型由ASP.NET 运行库决定,遵循约定文件名_扩展名。
ASP.net中动态加载控件时一些问题的总结
经常见到有人说在ASP.net中不要使用动态控件,我想主要的原因在于使用动态控件会带来一些问题,在做项目的过程中,我将由动态加载控件引发的总是作了一个小小的总结.
1 、在使用LoadControl加载控件后,用户控件中的某些控件不再响应事件。
这个问题主要是由于将控件加载放在if (!Page.IsPostBack)之内引起的,放在外面即可。在思归的blog上对此问题进行了详细的说明。
2、用户控件中某些控件的响应出现问题,如某个按钮第一次选择时不触发CLICK事件,第二次可以了。
这是由于没有给控件设置ID引起的,控件ID的作用在下面详细讲述。 如
Control userControl=(Control)Page.LoadControl(“Test.ascx”);
userControl.ID=“Test”;
AddControl(userControl);
3、如果用户控件中包括DataGrid控件,那么加载控件后可能出现不响应DataGrid事件的问题。
这好像是一个bug,必须要将加载的控件进行强制转换,如:
Test userControl=(Test)Page.LoadControl(“Test.ascx”);
注意:上面使用的是Test类型,而不是Control!
这种方式将使系统的扩展性降低。 我有一个解决方案可以和大家讨论(运用策略模式):
public class BaseControl : System.Web.UI.UserControl
{
public virtual BaseControl ProcessThisControl();
}
所有的用户控件从BaseControl 继承,如果有Datagrid控件,由overide ProcessThisControl方法 ,如:
return this as Test;
按如下方式加载控件:
BaseControl userControl=(BaseControl )Page.LoadControl(“Test.ascx”);
userControl.ProcessThisControl();
4、在用户控件中如何使用JavaScript。
大家都知道,使用客户端的脚本将大大提高页面的响应速度,同时可以避免频繁地刷新页面。所以使用javascript来实现页面中部分控制是一个比较好的方式,但是在用户控件中如果访问某一个子控件呢?
使用方式如下: document.all.<%= TestControl.ClientID%>
.disabled=true; //将TestControl设置为不可用
如果在C#脚本中应该这样写:Page.RegisterStartupScript("OnInitControl","<SCRIPT LANGUAGE='JavaScript'>document.all.Test_TestControl.disabled=true;</SCRIPT>"); //Test为用户控件,TestControl为用户控件中的子控件。
现在说一下控件ID,在访问aspx文件时,IIS会将aspx的脚本进行编译。编译的时候将用户控件中的内容写在同一个页面中,为了防止页面中的控件与用户控件中的控件名称相同,在编译的时候对用户控件中的控件名称修改为 : 用户控件名:子控件 ,控件ID则修改为 用户控件ID_子控件ID。 在动态加载控件时,如果不对控件的ID进行赋值,则控件ID为上一次加载的控件ID,因此在加载用户控件后应该立即对其设置ID。
复合控件
自描绘控件
我们可以在工程中增加一个类文件myRender.cs,使其继承自Control类,然后我们再重写Rnder方法。注意在VS2005中默认是不添加命名空间的,你可以自己添加,方便在网页中引用。
代码如下:
自定义类:
namespace myUser
{
public class myRender : Control
{
public myRender()
{
}
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<input type=button value='Click me'/>");
base.Render(writer);
}
}
}
页面文件:注意两个地方
<%@ Register TagPrefix="my" Namespace="myUser"%>
<form id="form1" runat="server">
<div>
<my:myRender runat="server" id="A1205"></my:myRender>
</div>
</form>
从客户端来看,形成如下代码:
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTQ1MjY2MDgxN2RktGopiS3qzznWpNtYV9oQKf4kTRw=" />
</div>
<div>
<input type=button value='Click me'/>
</div>
如果要返回一个在客户端事件中可以使用的字符串,可以定义如下
writer.Write("<input type=button value='Click me' οnclick='{0}'/>", Page.GetPostBackEventReference(this));
这是客户端的代码如下:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTQ1MjY2MDgxN2RktGopiS3qzznWpNtYV9oQKf4kTRw=" />
</div>
<div>
<input type=button value='Click me' οnclick='__doPostBack('A1205','')'/>
</div>
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
</div>
<script type="text/javascript">
<!--
var theForm = document.forms['form1'];
if (!theForm) {
theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
// -->
</script>
如果要在自描述控件处理回发事件,必须继承IPostBackEventHandler接口:
public class myRender : Control,IPostBackEventHandler
下面的代码示例定义一个自定义按钮服务器控件,该控件可引起回发,使用 RaisePostBackEvent 方法捕获回发,并在服务器上引发 Click 事件
#region IPostBackEventHandler
// Defines the Click event.
public event EventHandler Click;
//Invoke delegates registered with the Click event.
protected virtual void OnClick(EventArgs e)
{
if (Click != null)
{
Click(this, e);
}
}
// Define the method of IPostBackEventHandler that raises change events.
public void RaisePostBackEvent(string eventArgument)
{
OnClick(new EventArgs());
}
#endregion
IPostBackEventHandler.RaisePostBackEvent 方法
该页将 eventArgument 参数的值传递给实现 IPostBackEventHandler接口的控件的 RaisePostBackEvent 方法。此控件还会呈现导致发生回发的 HTML 元素。如果控件呈现了用于回发的客户端脚本,则会在 eventArgument 参数中传递脚本中的参数。如果回发是由简单提交操作所引起的,则 eventArgument 参数为 空引用(在 Visual Basic 中为 Nothing)。
在创建需要检查由客户端回发到服务器的窗体数据的服务器控件时,必须实现 IPostBackDataHandler接口。此接口定义的协定允许服务器控件确定其状态是否应作为回发的结果而发生更改,并引发相应的事件。
代码如下(增加一个IPostBackDataHandler接口):
public class myRender : Control, IPostBackEventHandler, IPostBackDataHandler
//定义属性
public String Text
{
get
{
return (String)ViewState["Text"];
}
set
{
ViewState["Text"] = value;
}
}
output.Write("<INPUT TYPE = submit name = " + this.UniqueID + " value = " + this.Text + "/>");
#region IPostBackDataHandler
public event EventHandler TextChanged;
public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
String presentValue = Text;
String postedValue = postCollection[postDataKey];
if (presentValue == null || !presentValue.Equals(postedValue))
{
Text = postedValue;
return true;
}
return false;
}
public virtual void RaisePostDataChangedEvent()
{
OnTextChanged(EventArgs.Empty);
}
protected virtual void OnTextChanged(EventArgs e)
{
if (TextChanged != null)
TextChanged(this, e);
}
#endregion
IPostBackDataHandler.LoadPostData 方法
如果服务器控件的状态由于回发而发生更改,则为 true;否则为 false。ASP.NET 页框架跟踪所有对此方法调用返回 true 的服务器控件,然后对这些控件调用 RaisePostDataChangedEvent 方法。