上一篇:
复合控件和事件(2)——属性,页面要回发,属性要保存 【
本文的例子以CompositeControl来命名,但不代表本文是描述复合控件,只是这个系列都在描述这个而已,本文在描述的是控件的制作过程而非复合控件,因此命名只是为了保持解决方案的美观(真不理解自己为了美而放弃了事实,大家就将就一下哈!记住这不是复合控件只是控件)至于在复合控件的文章里面提到这个,理由在文章的和字之后,也就是事件。所以请您仔细阅读咯。后续的文章将会建立在此基础之上。】每次当我们拿到Button的时候我们可以非常轻松地拖动它到页面,然后双击它就可以出现以下代码:
protected void Button1_Click(object sender,EventArgs e)
{
}
你是否正确理解它了呢?如果没有,请继续下一段,如果很了解请跳过。
也许你会想这样一句话有什么好说明的,但其实它也是很重要的。它是一个函数,但是它更代表了一种固定于事件的函数,这种函数我们称之为事件处理程序。每当我们“按下按钮”事件被激发的时候,我们就会执行这里面的代码。事件处理程序的返回值是void的,用C#入门经典里面对返回值的描述是:
因此我们应该在这里使用void,Button1_Click是一个事件处理程序的名字,其实只是名字,你要换成AAABBBCCC也是可以的,当然你一定不会这么做的。object sender,EventArgs e在MSDN里面讲的很清楚了,就直接贴了:引发事件的源和该事件的数据。(
事件和委托)说白了就是谁引发了事件,传递了什么数据。系统定义了一个EventArgs作为事件数据的基类,你可以定义自己的事件数据类,用于在事件处理的过程中传输数据。
那当你按下按钮的时候是如何得知事件被触发的呢?
先不要管“按钮按下的时候”这个关键字,先来看看事件一般都是如何触发的。
首先会有一个委托名为
public delegate void ××××EventHandler(object sender, ×××EventArgs e)
然后定义一个该委托类型的事件:
public event ××××EventHandler EventName
之后还有一个事件处理程序:
protected void OnEventName(×××EventArgs e)
{
……
}
事件是作为当前类的一个成员而存在的,我们可以在当前类中去引发它,应该理解为:在****条件下,我们会去处理我们的这个事件!而为了让这个事件的处理方式由外部提供,我们使用了委托来将具体的操作留给类的调用者,因此,我们在上面事件处理程序中的代码中不应该是具体解决问题的代码,而是一个委托,因此事件处理程序通常可以写为:
protected void OnEventName(×××EventArgs e)
{
if(EventName!=null)//如果事件存在的话
{
EventName(this,e);
}
}
而在程序中的某个地方则会调用到OnEventName这个函数,并将其所需要的数据添加进e里,并传递给那个外部被委托的那个函数。也就是为什么我们能在外部使用e加上点运算符得到具体数据的原因了。
根据委托的定义,我们在使用它的时候还需要添加如下语句:
ClassName obj = new ClassName();
//订阅事件
obj.××××EventHandler += new ××××EventHandler(
Method_Handler);
或者C#2.0内可以直接简化为:
obj.××××EventHandler += Method_Handler;
//事件处理程序
protected void Method_Handler(object sender, ×××EventArgs e)
{
//使用者需要在事件被触发的时候需要实现的代码
}
而到我们控件的处理上,当我们拖动控件的时候,事实上
ClassName obj = new ClassName();这句话的内容在****.aspx.designer.cs中,而订阅事件的部分也在<asp:…… OnEventName="Method_Handler" ……/>的句子中声明了,在页面预编译的时候将转换成订阅事件的标准IL代码。因此我们可以在我们使用控件的时候简单到只要有事件处理程序就可以了。
说了这么多其实只是把简单的事件运行的模型从语言层向整个以VS为开发平台的方向推了推,或者让开发者更了解自己做的是哪部分内容。
刚才还遗留了一个问题“按钮按下的时候”。这是什么问题呢?是这样的,事实上我们向页面添加一个服务端的按钮,运行的时候,我们添加的代码将从:
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Button" />
转换为:
<input type="submit" name="Button2" value="Button" id="Button2" />
这个过程看上去像是翻译,而事实上在控件的内部是将asp的那些代码中的Attribute填充到Html的控件中,比如Text="Button",就被映射到了value,runat="server"则体现了type="submit"的HTML标签,这些HTML标签从服务器传回了客户端,客户端通过IE等浏览器,对HTML进行解析,就可以得到我们所见到的丰富多彩的网页了。 说到这里你似乎还是没法理解“按钮按下的时候”这个问题,其实当HTML在传回到客户端后就和服务器之间失去了联系,那么“按钮按下的时候”事实上只是一个完全客户端的事情。如果你对HTML有所了解的话你会发觉这里的type="submit"指示了这个按钮不是只代表了一个看上去很有弹性的方块,它还表示它按下的时候与type="button"类型按钮的区别在于它会导致整个页面回发(仔细看浏览器下方中间(如果是IE的话)的进度条会有一次刷新,如果是button则没有任何反应)。页面回发会向服务端再次请求当前页面,也就是这次请求让客户端与服务端再次地彼此联系。关于页面回发,下面是MSDN的一些描述:
回发和往返行程
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_aspnetcon/html/ed82ea8a-1439-4289-85d6-5ac86a012dbb.htm
ASP.NET 页面作为代码在服务器上运行。因此,要得到处理,页面必须配置为当用户单击按钮(或者当用户选中复选框或与页面中的其他控件交互)时提交到服务器。每次页面都会提交回自身,以便它可以再次运行其服务器代码,然后向用户呈现其自身的新版本。
ASP.NET 网页的处理循环如下:
1.用户请求页面。(使用 HTTP GET 方法请求页面。)页面第一次运行,执行初步处理(如果您已通过编程让它执行初步处理)。
2.页面将标记动态呈现到浏览器,用户看到的网页类似于其他任何网页。
3.用户键入信息或从可用选项中进行选择,然后单击按钮。(如果用户单击链接而不是按钮,页面可能仅仅定位到另一页,而第一页不会被进一步处理。)
4.页面发送到 Web 服务器。(浏览器执行 HTTP POST 方法,该方法在 ASP.NET 中称为“回发”。)更明确地说,页面发送回其自身。例如,如果用户正在使用 Default.aspx 页面,则单击该页上的某个按钮可以将该页发送回服务器,发送的目标则是 Default.aspx。
5.在 Web 服务器上,该页再次运行。并且可在页上使用用户键入或选择的信息。
6.页面执行您通过编程所要实行的操作。
7.页面将其自身呈现回浏览器。
数据终于千里迢迢从客户端回到了服务端。服务端又如何知道该怎么做呢?由于是回发事件,了解回发的控件是必须要实现IPostBackEventHandler接口的(如果是类似TextBox中数据改变等的则需要实现IPostBackDataHandler接口。控件可引起回发,使用 RaisePostBackEvent 方法捕获回发。RaisePostBackEvent 是IPostBackDataHandler接口的一个方法,也是唯一的一个公共方法。RaisePostBackEvent当由类实现时,使服务器控件能够处理将窗体发送到服务器时引发的事件。也就是当有回发发生的时候会调用这个程序。我写了一个代码来验证它:
IPostBackEventHandler 成员#region IPostBackEventHandler 成员
public void RaisePostBackEvent(string eventArgument)
{
Page.Response.Write("RaisePostBackEvent");
}
#endregion
既然回发的时候这个片段会被调用到,那么我们是不是应该乘机将我们定义的一堆事件处理程序一并处理掉呢?答案是当然的。所以我们可以将我们在程序中定义的事件处理程序在这里分享给外部,而之前我们讨论过,我们的事件处理程序其实只是将我们要做的事情委托给控件类的外部,那么实际上要处理的代码也就是我们通常在××××.aspx.cs文件中所做的处理程序比如:Button1_Click(object sender,EventArgs e)这段再也熟悉不过的代码了。于是以上程序应该被写为:
IPostBackEventHandler 成员#region IPostBackEventHandler 成员
public void RaisePostBackEvent(string eventArgument)
{
//Page.Response.Write("RaisePostBackEvent");
OnControlClick(new ControlEventArgs());
}
#endregion
这下应该明白,为什么我们可以轻松地去实现按钮按下的事件了,理由是MS为我们提供了IPostBackEventHandler。(其实IPostBackDataHandler也是基于同样的使用方式,只是里面的方法不同而已。)
IPostBackDataHandler专门用来处理有数据回发的程序的,我们让我们的类去实现这个接口,Render方法内添加以下语句:
writer.Write("<input id=\"Text1\" name=\"" + this.UniqueID + "\" type=\"text\" value=\"" + this.Text + "\" /><br>");
另外添加一个Text属性和实现IPostBackDataHandler的方法:
public String Text
{
get
{
if (ViewState["Text"] != null)
return (String)ViewState["Text"];
else
return string.Empty;
}
set
{
ViewState["Text"] = value;
}
}
IPostBackDataHandler 成员#region IPostBackDataHandler 成员
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
String presentValue = Text;
String postedValue = postCollection[postDataKey];
if (presentValue == null || !presentValue.Equals(postedValue))
{
Text = postedValue;
return true;
}
return false;
}
public void RaisePostDataChangedEvent()
{
ControlEventArgs e = new ControlEventArgs();
e.Message = "(In RaisePostDataChangedEvent)";
OnControlTextChanged(e);
}
#endregion
这些代码您可以通过MSDN和asp.net控件开发基础(3) 文章来清晰地了解,这里将不再重复这个过程。下面是控件现在的截图:
点击submit后,则是:
填写数据后再点submit的结果:
为什么会无端多出submit呢?
我们可以断点调试这个问题(过程不展示了,比较罗嗦),经过分析发现问题所在:
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
就在这个postDataKey中,记得我们是如何命名我们的控件么?不记得了那就让我们看看最后生成的控件HTML:
<INPUT TYPE=button name=CompositeControl3_1 Value='确定' />
<input id="Submit1" name="CompositeControl3_1" type="submit" value="submit" /><br>
<input id="Text1" name="CompositeControl3_1" type="text" value="" />
其中它们都是name为CompositeControl3_1,其中Type=button的由于只是处于客户端的一个按钮,它的name其实无什么作用(name通常只被用于服务端控件,id则用于客户端)。而后两个name我们都是通过this.UniqueID来将其赋值的,这些让我们有些遭殃。因为postDataKey在断点时表现为CompositeControl3_1,而LoadPostData则通过postCollection来处理。如果只有一个Text就无所谓,但这里又多了一个Button也是同名的,这时候postedValue的值会将所有这个名字的服务端控件的value值用“,”(逗号)连接起来,类似:submit,填写结果
【MSDN】postCollection :所有传入名称值的集合。
改造1(无意义):可以将其name做一些手脚,让与Text的name区别于别人,这样可以达到效果,但是在之后的实现中,事件处理程序将不可用。理由是“它已经不再是它本身,本身的事件处理程序自然不属于它管”。
改造2:只取需要的数据:(将Text框内的数据单独取出)
这个问题似乎解决了,但是新的问题又来了。不知道你是否正在同步演练这个示例,如果是的话你一定注意到submit自己的事件处理程序不再被调用了。为什么呢?
在IPostBackEventHandler与IPostBackDataHandler同时实现的时候IPostBackEventHandler 会得不到响应。IPostBackDataHandler提前捕获了回发,让IPostBackEventHandler无法感应到了。这个时候我们需要手动补充注册一下:
Page.RegisterRequiresRaiseEvent(this);
这句话加在哪里呢?既然是IPostBackDataHandler捕获了,而它捕获后的第一件事就是LoadPostData,因此我就将它放置于此了。由于是Page的一个方法,则必然Page要存在,当然Page一定是在的,因为是回发嘛,回发一定有页面存在咯。所以有:
当然了,这个添加的位置可以在其他地方,这个你可以自己尝试,我只是提出了一种可以解决问题的答案。(您可以参考撰写与呈现一文)
问题真的解决了吗?(以下过程需要添加两个相同的该控件)同步演练时你一定会发现CompositeControl3_1的ControlClick事件已经沦陷咯(不起作用了)!理由呢?
注释掉下面的句子你再试试:
writer.Write("<input id=t" + this.UniqueID + " name=" + this.UniqueID + " type=\"text\" value=\"" + this.Text + "\" /><br>");
一切就恢复正常了。
原因是我们的页面中只有一个name=CompositeControl3_1,也只有一个name=CompositeControl3_2。但是如果我们补充了上面这句,则上面两个name的标签将出现两次,类似:
<INPUT TYPE=button name=CompositeControl3_1 Value='确定' /><input id=CompositeControl3_1 name=CompositeControl3_1 type="submit" value="submit" /><br><input id=CompositeControl3_1 name=CompositeControl3_1 type="text" value="" /><br>
我们平时是如何使用asp控件的时候我们是不允许它们的ID相同的,也就是最后生成的页面中name必须是唯一值:
//aspx页面中添加以下代码,将会出现设计时错误以及编译不通过的尴尬境地
<asp:Button ID="Button1" runat="server" Text="Button" />
<asp:Button ID="Button1" runat="server" Text="Button" />
刚才我们用了Page.RegisterRequiresRaiseEvent(this);注册事件,让Event可以外部所激活,this先后顺序分别是CompositeControl3_1和CompositeControl3_2,也就是最后一次是CompositeControl3_2的事件被Raise了。注意MSDN中对于RegisterRequiresRaiseEvent的描述:
每个页请求只能注册一个服务器控件。当窗体发送数据中不包括控件的控件 ID 时,必须使用 RegisterRequiresRaiseEvent。而且,注册的控件必须实现 IPostBackEventHandler 接口。
也就是说这里只会执行CompositeControl3_2的事件。因此其CompositeControl3_1的ControlClick就会doesn't work了。
将Render中TextBox的语句修改为:
writer.Write("<input id=" + this.UniqueID + "_t name=" + this.UniqueID + "_t type=\"text\" value=\"" + this.Text + "\" /><br>");
运行的结果配合:Page.RegisterRequiresRaiseEvent(this);的结果则是OK的。理由是因此之前控件name重名了,LoadPostData就会被执行N次,N=控件的数量。也就是每次都会有不同的postDataKey进来,分别是CompositeControl3_1、CompositeControl3_2……它会将所有Post的数据进行一次检查,Page.RegisterRequiresRaiseEvent(this);的最终结果也就是最后一个事件了。但是如果名字正确会怎样呢?答案就是按哪哪应。也就是LoadPostData只执行一次,因此Page.RegisterRequiresRaiseEvent(this);的结果也就是当前的那个按钮了。至此我们可以配合以下代码正确运行程序了。
Page.RegisterRequiresRaiseEvent(this);
String presentValue = Text;
String postedValue = postCollection[postDataKey + "_t"];
而且因为Page.RegisterRequiresRaiseEvent(this);这个句子是在页面回发的时候才执行的,而执行的过程中我们保证了它只被用于当前控件,因此所谓的每个页请求只能注册一个服务器控件在这里就可以得到满足了。
也正是因为名字的唯一化,GetText的函数从此可以直接注释掉咯!
方案二:既然我们可以按哪哪应,而且不管是IPostBackEventHandler还是IPostBackDataHandler 都会感应到回发事件,一旦回发,分别会有RaisePostBackEvent和LoadPostData为它们响应。看看IPostBackEventHandler的作用也无非就是将相关事件进行一次处理而已,Page.RegisterRequiresRaiseEvent(this);的唯一目的也就是将RaisePostBackEvent执行一遍,因此可以将Page.RegisterRequiresRaiseEvent(this);的位置直接替换成OnControlClick(new ControlEventArgs());——>测试,通过!
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
//回发的时候对其进行事件处理
OnControlClick(new ControlEventArgs());
String presentValue = Text;
String postedValue = postCollection[postDataKey + "_t"];
//postedValue = GetText(postedValue, 1);
if (presentValue == null || !presentValue.Equals(postedValue))
{
Text = postedValue;
return true;
}
return false;
}
另外还有事件数据方面的问题需要小小地说明:事件数据是和事件相关地数据(废话),在事件处理程序中传递着,它继承于EventArgs(前面已经提及)。如果你有这方面的需要的话则可以自己写一个自己的事件数据类(感觉就像一个实体类,当然你可以有自己的方法,这方面很灵活):
public class ControlEventArgs : EventArgs
{
private string message = string.Empty;
public string Message
{
get
{
return message ;
}
set
{
message = value;
}
}
}
代码:
CompositeControl3.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace ComponentWebControls
{
[DefaultEvent("ControlClick")]
[ToolboxData(@"<{0}:CompositeControl3 runat='server' />")]
public class CompositeControl3 : Control, INamingContainer, IPostBackEventHandler, IPostBackDataHandler
{
/**//// <summary>
/// 定义ControlClick事件
/// </summary>
[Category("ControlClick"), Description("ControlClick")]
public event ControlEventHandler ControlClick;
/**//// <summary>
/// ControlClick的事件处理程序
/// </summary>
/// <param name="e"></param>
protected virtual void OnControlClick(ControlEventArgs e)
{
if (ControlClick != null)
{
ControlClick(this, e);
}
}
/**//// <summary>
/// 定义ControlTextChanged事件
/// </summary>
[Category("ControlTextChanged"), Description("ControlTextChanged")]
public event ControlEventHandler ControlTextChanged;
/**//// <summary>
/// ControlTextChanged的事件处理程序
/// </summary>
/// <param name="e"></param>
protected virtual void OnControlTextChanged(ControlEventArgs e)
{
if (ControlTextChanged != null)
{
ControlTextChanged(this, e);
}
}
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<INPUT TYPE=button name=" + this.UniqueID + " Value='确定' />");
writer.Write("<input id=" + this.UniqueID + " name=" + this.UniqueID + " type=\"submit\" value=\"submit\" /><br>");
writer.Write("<input id=" + this.UniqueID + "_t name=" + this.UniqueID + "_t type=\"text\" value=\"" + this.Text + "\" /><br>");
//base.Render(writer);
}
IPostBackEventHandler 成员#region IPostBackEventHandler 成员
public void RaisePostBackEvent(string eventArgument)
{
//if (eventArgument == this.ClientID)
/**//*配合以下代码使用
* //if (IsPostBack)
* //{
* // this.RaisePostBackEvent(this.CompositeControl3_1, "CompositeControl3_1");
* //}
*/
OnControlClick(new ControlEventArgs());
}
#endregion
public String Text
{
get
{
if (ViewState["Text" + this.UniqueID] != null)
return (String)ViewState["Text" + this.UniqueID];
else
return string.Empty;
}
set
{
ViewState["Text" + this.UniqueID] = value;
}
}
IPostBackDataHandler 成员#region IPostBackDataHandler 成员
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
//同时实现IPostBackEventHandler和IPostBackDataHandler需要手动注册
//将 ASP.NET 服务器控件注册为需要在 Page 对象上处理控件时引发事件的控件。
//由于该方法一定是在回发的时候才会有所作为,因此Page对象一定!=null.
//Page.RegisterRequiresRaiseEvent(this);
OnControlClick(new ControlEventArgs());
String presentValue = Text;
String postedValue = postCollection[postDataKey + "_t"];// + "_t"
//postedValue = GetText(postedValue, 1);
if (presentValue == null || !presentValue.Equals(postedValue))
{
Text = postedValue;
return true;
}
return false;
}
private string GetText(string controlText,int textIndex)
{
string[] strArray = controlText.Split(new char[] { ',' });
if (strArray.Length > textIndex)
return strArray[textIndex];
else
return controlText;
}
public void RaisePostDataChangedEvent()
{
ControlEventArgs e = new ControlEventArgs();
e.Message = "(In RaisePostDataChangedEvent)<br>";
OnControlTextChanged(e);
}
#endregion
}
}
CompositeControl3.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CompositeControl3.aspx.cs" Inherits="WebAppTestControls.CompositeControl3"%>
<%@ Register Assembly="ComponentWebControls" Namespace="ComponentWebControls" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>CompositeControl3</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<cc1:CompositeControl3 ID="CompositeControl3_1" runat="server" OnControlClick="CompositeControl3_1_ControlClick" OnControlTextChanged="CompositeControl3_1_ControlTextChanged">
</cc1:CompositeControl3>
<br />
<asp:Label ID="LabelResult" runat="server" Text="页面刚加载!"></asp:Label><br />
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Button" /> <br />
<cc1:CompositeControl3 ID="CompositeControl3_2" runat="server" OnControlClick="CompositeControl3_2_ControlClick" OnControlTextChanged="CompositeControl3_2_ControlTextChanged">
</cc1:CompositeControl3>
<br />
<cc1:CompositeControl3 ID="CompositeControl3_3" runat="server" OnControlClick="CompositeControl3_3_ControlClick"
OnControlTextChanged="CompositeControl3_3_ControlTextChanged">
</cc1:CompositeControl3>
<br />
<br />
</div>
</form>
</body>
</html>
CompositeControl3.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
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;
namespace WebAppTestControls
{
public partial class CompositeControl3 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//this.CompositeControl3_1.ControlClick += CompositeControl3_1_ControlClick;
//this.CompositeControl3_1.ControlClick += new ComponentWebControls.ControlEventHandler(CompositeControl3_1_ControlClick);
//if (IsPostBack)
//{
// this.RaisePostBackEvent(this.CompositeControl3_1, "CompositeControl3_1");
//}
}
protected void Button2_Click(object sender, EventArgs e)
{
this.LabelResult.Text += "Button2_Click<br>";
}
protected void CompositeControl3_1_ControlClick(object sender, ComponentWebControls.ControlEventArgs e)
{
this.LabelResult.Text += "CompositeControl3_1_ControlClick<br>";
}
protected void CompositeControl3_1_ControlTextChanged(object sender, ComponentWebControls.ControlEventArgs e)
{
this.LabelResult.Text += e.Message;
}
protected void CompositeControl3_2_ControlClick(object sender, ComponentWebControls.ControlEventArgs e)
{
this.LabelResult.Text += "CompositeControl3_2_ControlClick<br>";
}
protected void CompositeControl3_2_ControlTextChanged(object sender, ComponentWebControls.ControlEventArgs e)
{
this.LabelResult.Text += "CompositeControl3_2_ControlTextChanged<br>";
}
protected void CompositeControl3_3_ControlClick(object sender, ComponentWebControls.ControlEventArgs e)
{
this.LabelResult.Text += "CompositeControl3_3_ControlClick<br>";
}
protected void CompositeControl3_3_ControlTextChanged(object sender, ComponentWebControls.ControlEventArgs e)
{
this.LabelResult.Text += "CompositeControl3_3_ControlTextChanged<br>";
}
}
}
CompositeControl3.aspx.designer.cs
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.42
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace WebAppTestControls {
/**//// <summary>
/// CompositeControl3 类。
/// </summary>
/// <remarks>
/// 自动生成的类。
/// </remarks>
public partial class CompositeControl3 {
/**//// <summary>
/// form1 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
/**//// <summary>
/// CompositeControl3_1 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::ComponentWebControls.CompositeControl3 CompositeControl3_1;
/**//// <summary>
/// LabelResult 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::System.Web.UI.WebControls.Label LabelResult;
/**//// <summary>
/// Button2 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::System.Web.UI.WebControls.Button Button2;
/**//// <summary>
/// CompositeControl3_2 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::ComponentWebControls.CompositeControl3 CompositeControl3_2;
/**//// <summary>
/// CompositeControl3_3 控件。
/// </summary>
/// <remarks>
/// 自动生成的字段。
/// 要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
/// </remarks>
protected global::ComponentWebControls.CompositeControl3 CompositeControl3_3;
}
}
ControlEvent.cs
using System;
namespace ComponentWebControls
{
//定义委托
public delegate void ControlEventHandler(object sender, ControlEventArgs e);
//定义事件数据类
public class ControlEventArgs : EventArgs
{
private string message = string.Empty;
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
}
至此事件与控件的结合就显得相对明晰了。本文描述了详细的演化过程,大家会不会觉得有点混乱呢?如果会,请一定回头跟着演练,因为我的表达不保证到位,希望大家至少能够意会,别让我的言传让您误会噢~!^.^
有一篇文章不错,推荐给大家Understanding ASP.NET View State其中对页面生命周期的描述相当地详尽,其中一些涉及的知识点都是很有用的。