原理是这样的,如果自己写代码应该弄的清楚一些,另外,按钮委托和事件.net已经定义好了,不需要自己定义。
如果用VS写,从属性窗口黄色闪电的图标,里面可以选事件。
如果用VS写,从属性窗口黄色闪电的图标,里面可以选事件。
解决方案 »
- 求一正则表达式,匹配花括号的嵌套
- ASP.net做的网站在IE8中如何居中
- CSDN为什么不出一个手机客户端啊,那样用着多方便
- gridview中分页如何返回的问题
- asp.net
- 为什么我的SQL语句没有执行啊,头疼了两三天了!!!!!!!!!!!!!!
- MagicAjax的哪个loading可以改变他的位置和文字吗
- 500分求解超难问题,能成功外理的再开4个贴给分
- 关于Microsoft Visual Studio .NET 2003站点目录的问题?
- 求一简单sql语句,解决马上给分??
- 如何从url中获得信息
- About Crystal Report,Saucer and others ,thanks!
你自己理解一下这个例子吧!<% @ Page Language="C#" %>
<Script Language="C#" Runat="Server">
public void Button_Click(Object sender,EventArgs e)
{
string argName = ((Button)sender).CommandName;
string argArg = ((Button)sender).CommandArgument;
Label1.Text="您选中的动作为:<font color=red>"+argName+"</font>,动作目标是:<font color=red>"+argArg+"</font>";
}
</script>
<html>
<head>
<title></title>
</head>
<body>
<b>Button控件演示</b>
<hr>
<form runat="server">
<asp:Button id="Button1" CommandName="踢" CommandArgument="足球" Text="动作1" runat="server" OnClick="Button_Click" /><br>
<asp:Button id="Button2" CommandName="打" CommandArgument="篮球" Text="动作2" runat="server" OnClick="Button_Click" /><br>
<hr>
<asp:Label id="Label1" runat="server" />
</form>
</body>
</html>
button2.Click+=new EventHandler(Click2);//此处也可以调用Click1,那样就相同的处理
button3.Click+=new EventHandler(Click3);protected void Click1(object sender,EventArgs e)
{
处理单击的内容
}VS自动生成的也是这样的,自己写代码也一样。
关键字 C# Events
下面的文章是我自认为对"事件"机制理解比较透彻的后写的代码分析,但写完之后,鄙人又惟恐理解有所偏差,所以特贴出来让各位高手指教一二,若能让发现理解错误之处,将不胜感激(此问完全乃本人"独立自主"之作,绝非抄袭)同时我相信此文对c#初学者也有一定帮助!为了阐述清晰,特举例说明;
该范例是在一个控件中完全自定义一组事件,并在另外的程序集中对事件被激发作出反映(也就是事件被激发后调用预先定义好的方法).一.含有自定义事件的控件主体代码及对应剖析 (注意,此控件库是由VS.NET的"新建"->"Windows控件库"生成的)namespace MyEventTEST
{
public class LoginEventArgs : System.EventArgs
// 上面代码定义了在主程序中引发事件时需要传递给主程序的所有信息,并且注意,
// 该类必须派生于System.EventArgs类
{
public LoginEventArgs(string sUserID, string sPassword, bool bValid)
{
UserID = sUserID;
Password = sPassword;
Valid = bValid;
} public string UserID;
public string Password;
public bool Valid;
} public delegate void GoodLoginEventHandler(object sender, LoginEventArgs e);
public delegate void FailedThreeTimesEventHandler(object sender, LoginEventArgs e);
// 上面两行代码定义了两个多路委托(因此返回类型必须为void),每个委托对应一种类型的事件;
// 因为是多路委托,所以每个委托中可以含有多个方法.
// 请注意,参数是(object sender, LoginEventArgs e),所以添加到多路委托的方法必须符合这种签名方式.
// 此外,为什么这里不使用系统已经定义的多路委托"System.EventHandler(object sender, EventArgs e)",
// 而要自己定义新的委托"?????EventHandler()"呢?这是因为我们这里传递给用户程序集的参数不是
// "System.EventArgs"类型,而是自己定义的"LoginEventArgs"类型,所以有必要重新定义自己的委托类型.
public class ActiveLogin : System.Windows.Forms.UserControl
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txtUserID;
private System.Windows.Forms.TextBox txtPass;
private System.Windows.Forms.Button btnLogin;
private System.Windows.Forms.Button btnCancel;
private System.ComponentModel.Container components = null;
// 上面代码是组成这个控件的一些组件定义,由VS.NET自动生成 public event GoodLoginEventHandler GoodLogin;
public event FailedThreeTimesEventHandler FailedThreeTimes;
public event EventHandler Cancel;
// 上面三行代码非常之重要,定义了三个事件(event),分别是"GoodLogin","FailedThreeTimes"
// 和"Cancel"
// 它们的类型分别是"GoodLoginEventHandler","FailedThreeTimesEventHandler"
// 和"EventHandler",也就是说添加到这三个事件中的方法必须符合对应的多路委托定义!
// 而且注意,因为事件"Cancel"的类型是系统已经定义的多路委托"EventHandler"类型,
// 所以上面的多路委托中没有定义类似"CancelEventHandler"的新委托,因为是不需要的.
public ActiveLogin()
{
InitializeComponent();
}
// 上面代码是控件中类"ActiveLogin"的构造方法,该方法中调用了初始方法InitializeComponent()
// 上面代码由VS.NET自动生成 protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
// 上面代码是自定义控件中类"ActiveLogin"的析构方法,由VS.NET自动生成. private void InitializeComponent()
{
.... // 这里是对所有引用控件(组件)的初始化代码
}
// 上面代码是自定义控件中类"ActiveLogin"的初始方法,其中内容由VS.NET自动生成. protected virtual void OnGoodLogin(LoginEventArgs e)
// 上面一行代码定义了激发"GoodLogin"事件的方法;
// 注意签名类型,定义方法是protected virtual,也就是说只能在这个类及它的
// 继承类中访问此方法,而且可以重写.
// 参数类型是"LoginEventArgs",注意只有这一个参数,因为在本方法中含有对
// this的引用,所以这里不需要传递this对象.
// 一般地说,这个方法使用场合只有两种:
// <1>在本控件内被调用,因为本方法不被调用,就无法激发用户代码在事件"GoodLogin"
// 中添加的方法;
// <2>在用户的继承代码中重写本方法,虽然重写本方法可能会带来性能的提高,但
// 倘若用户代码中忘记调用此方法,那么在用户代码先前在事件"GoodLogin"中
// 添加的方法将无法得到激活!!! (避免此问题的方法就是在重写方法中必须含有
// 一行"base.GoogLogin(e)",这行将负责调用本方法)
// 对于第<2>点需要提出的是,在用户的继承代码中重写本方法的作用相当与在
// 事件"GoodLogin"中添加一个方法,此方法的代码内容和重写方法内容相同.
// (但是应该绝对没有"base.GoogLogin(e)"这一行)
{
if (GoodLogin != null) // 如果在事件"GoogLogin"中含有方法,则激发这些方法
{
GoodLogin(this, e); // 把this对象和参数e传递给所有在事件"GoogLogin"
// 中添加的方法,并顺序执行这些方法 (注意,由多路
// 委托特性决定:在用户代码中先添加的方法先执行.
}
}
// 上面对OnGoogLogin方法解释已经十分详细了,下面两个ON方法均与上述ON方法同出一辙. protected virtual void OnFailedThreeTimes(LoginEventArgs e)
{
if (FailedThreeTimes != null)
{
FailedThreeTimes(this, e);
}
} protected virtual void OnCancel(System.EventArgs e)
{
if (Cancel != null)
{
Cancel(this, e);
}
}
private void btnLogin_Click(object sender, System.EventArgs e)
// 上面的定义是由VS.NET自动生成,是当按下控件的"btnLogin"按钮时调用的方法.
{
if(...)
OnGoodLogin(new LoginEventArgs(txtUserID.Text, txtPass.Text, true));
// 上面一行代码调用了OnGoodLogin方法,作用是"当控件中的按钮btnLogin被按下时,
// 并且符合上面的if条件时:
// 将通过调用OnGoodLogin方法把在用户代码中添加到事件"GoogLogin"中的所有方法
// 全部顺序执行一遍.
// 为什么不在这里把OnGoodLogin()方法中的代码执行一遍,而还要再单独调用OnGoodLogin
// 方法呢? 这是因为有时候用户代码需要重写OnGoodLogin()方法!
// 下面调用的OnFailedThreeTimes()方法和OnCancel()方法解释同上.
else
OnFailedThreeTimes(new LoginEventArgs(txtUserID.Text, txtPass.Text, false));
} private void btnCancel_Click(object sender, System.EventArgs e)
{
OnCancel(new EventArgs());
}
}
}
二.调用此控件的程序集(注意,此程序集是由VS.NET的"新建"->"Windows应用程序"生成的),也就是"用户代码"部分namespace HostApp
{
public class Form1 : System.Windows.Forms.Form
{
private MyEventTEST.ActiveLogin activeLogin1;
// 上面一行代码引用了自定义控件库的类"ActiveLogin",并用它定义了一个对象"activeLogin1".
// 这里的"MyEventTEST"是在自定义的控件库中定义的命名空间,如果在这个程序集中没有出现
// "using MyEventTEST"语句,则该名称必须出现在对自定义控件引用的任何代码中! private System.ComponentModel.Container components = null; public Form1()
{
InitializeComponent();
} protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
// 上面两个方法都是又VS.NET自动生成,不做另外解释. private void InitializeComponent()
{
this.activeLogin1 = new MyEventTEST.ActiveLogin();
// 上面一行代码用自定义控件库中的类"ActiveLogin"实例化对象"activeLogin1"
this.SuspendLayout(); //
// activeLogin1
//
this.activeLogin1.Location = new System.Drawing.Point(144, 8);
this.activeLogin1.Name = "activeLogin1";
this.activeLogin1.Size = new System.Drawing.Size(280, 184);
this.activeLogin1.TabIndex = 0; this.activeLogin1.GoodLogin += new MyEventTEST.GoodLoginEventHandler(this.activeLogin1_GoodLogin);
this.activeLogin1.Cancel += new System.EventHandler(this.activeLogin1_Cancel);
this.activeLogin1.FailedThreeTimes += new MyEventTEST.FailedThreeTimesEventHandler(this.activeLogin1_FailedThreeTimes);
// !!! 请注意上面的三行代码,这是用户代码接受自定义控件库中事件的代码 !!!
// 上面三行代码分别把用户定义的方法"activeLogin1_GoodLogin","activeLogin1_Cancel"
// 和"activeLogin1_FailedThreeTimes"分别添加到自定义控件中的事件"GoogLogin","Cancel"
// 和"FailedThreeTimes"中; 这样一来只要自定义控件中的对应事件一被激发,这些
// 添加用户自定义方法就会被执行.
// 要注意的是,用户自定义方法签名必须符合对应的多路委托的定义(因为事件是由多路委托
// 定义的,所以要添加到什么事件,定义就必须符合该事件对应的多路委托的定义)
// 而且,这里的Cancel事件类型是系统已经定义的多路委托"EventHandler"类型,所以它的实例化
// 与其它两个事件的实例化有所不同,是"System.EventHandler",而不是"MyEventTEST.EventHandler"!
// 这些用户自定义方法将在下面列出.
// 不过请注意,上面的三行代码虽然在方法InitializeComponent()中,但却是我们自己手工添加的! //
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(440, 357);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.activeLogin1,
});
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
} [STAThread]
static void Main()
{
Application.Run(new Form1());
}
// 上面的方法分别是Windows Forms程序的入口. private void activeLogin1_GoodLogin(object sender, MyEventTEST.LoginEventArgs e)
{
MessageBox.Show("Good Login! " + e.UserID);
} private void activeLogin1_FailedThreeTimes(object sender, MyEventTEST.LoginEventArgs e)
{
MessageBox.Show("Failed to login three times.");
} private void activeLogin1_Cancel(object sender, System.EventArgs e)
{
MessageBox.Show("Cancel");
}
// 上面的三个方法(activeLogin1_GoogLogin,activeLogin1_Cancel和activeLogin1_FailedThreeTimes)
// 就是当自定义控件中对应的事件被激发时在当前程序集中对应的处理方法.
// 值得注意的是,签名应该完全符合对应的多路委托的定义!
}
}
....behaviour.....
</script>
首先使用HtmlTextWriter类,名称为output写一个登录的html链接,代码:
//-----------------
output.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID + "lblSubmit");
//下面代码我使用了类似RequiredFieldValidator控件的法子,测试用户输入不为空
output.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:{if(ControlCheckValue())" + Page.GetPostBackEventReference(this, this.UniqueID & "_submit") & "}");
//下面是link的CSS样式
output.AddAttribute(HtmlTextWriterAttribute.Class, "mainlink");
//下面发送<a>
output.RenderBeginTag(HtmlTextWriterTag.A);
output.Write("登录");
//发送</a>
output.RenderEndTag();
//--------------------
假设此服务器控件ID为log1,那么服务器控件会在aspx网页上生成<a name="log1lblSubmit" href="javascript:{if(ControlCheckValue())__doPostBack('log1','log1_submit')}" class="mainlink">登录</a> 这样的html代码。我们需要注意的就是这里面的内容:
__doPostBack('log1','log1_submit')
此代码就是把这个普通的link变成了asp.net的linkbutton服务器控件,但是我们必须在后台代码中处理会送,否则按了这个按钮也没有用。
这时,需要利用IPostBackEventHandler和IPostBackDataHandler接口了。
AspNetPager实现的其它两个接口分别是IPostBackEventHandler和IPostBackDataHandler,前者处理回发事件,后者处理回发数据。AspNetPager是通过两种方式来对回发的数据进行处理的,一种就是点击分页导航按钮时引发的回发事件,该回发事件带了一个参数,这个参数值就是该按钮对应的页索引值,那么你是不是很想知道这个参数是如何传回来的呢?如果你用过LinkButton控件,你就会清楚这一点,LinkButton是怎么引发服务器事件的呢?查看aspx页发送到客户端的HTML代码,你会发现,在HTML的form标签下,多了两个隐藏的文本框,一个叫“__EVENTTARGET”,另一个叫“__EVENTARGUMENT”,而那个LinkButton控件呢?它转换成了一个超链接,它链接一个类似于“javascript:__doPostBack('btn','')”这样的Javascript函数,这个__doPostBack函数的代码是这样:function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();}其中的_ctl0是你的HtmlForm控件(即<form runat="server">标签)的客户端ID,如果你懂点Javascript就不理解这段代码,__doPostBack函数带了两个参数,一个叫eventTarget,一个叫eventArgument,它把eventTarget参数的值赋给我们前面提到的__EVENTTARGET隐藏文本框的value属性,把eventArgument赋给另一个同样的文本框__EVENTARGUMENT的value属性,然后提交表单,结合上面那个LinkButton转换成的超链接 javascript:__doPostBack('btn','')",你该明白这个LinkButton做什么了吧?它把自己的客户端ID提交回服务器端(eventArgument是空的),服务器端怎么处理这个回发呢?很简单,页框架从回发到服务器的参数上确定这是哪个LinkButton引发了回发事件,然后就把回发事件中传上来的参数传递给这个LinkButton的IPostBackEventHandler接口RaisePostBackEvent()方法,在该方法中执行该LinkButton的Click事件处理程序,再把结果页面发回客户端。
我们在服务器控件中可以使用两种方法获得javascript:__doPostBack的调用:
一是Page.GetPostBackEventReference()方法,此方法仅生成__doPostBack('controlname','argument')这样的html代码,而不是生成标准的javascript:__doPostBack('controlname,'argument')。
第二种方法就是Page.GetPostBackClientHyperlink()方法,生成的是标准提交代码。
我使用第一种方法是为了调用自定义的ControlCheckValue()js代码,进行输入检查。
反正无论是用哪种方法,一样可以把使用的参数和控件名称传递到后台,让asp.net运行库处理。服务器端页框架根据传回的值来看引发回发的控件是否需要自己处理回发事件(即是否实现IPostBackEventHandler接口),如果需要自己处理回发事件,页框架就会通知该控件并将回发的数据作为参数交给IPostBackEventHandler接口的RaisePostBackEvent()方法去处理,前面不是说了吗?我们给每个页导航元素都赋了一个参数,即该元素对应的页索引值,这样我们在RaisePostBackEvent()方法的参数中接收到的数据就是用户点击的页导航元素对应的页索引,我们用这个索引值来初始化PageChangedEventArgs这个传递数据给OnPageChanged()方法的EventArgs类,然后通过PageChangedEventHandler这个委托,把PageChanged事件和事件处理程序挂钩,在该事件处理程序中用户可以执行数据绑定等操作。前面只说了第一种处理回发数据的方法,另一种方法,也就是处理那个页索引输入文本框中的值的方法,是实现IPostBackDataHandler接口,该接口有两个方法,一个是LoadPostData(),用来接收回发的数据,一个是RaisePostDataChangedEvent(),它是用来通知回发数据的控件该控件的状态(如输入到TextBox中的值)已更改,如果控件状态更改后你需要执行某些操作,可以在该方法中执行。是否执行RaisePostDataChangedEvent()方法是由LoadPostData()方法的返回值来决定的,如果LoadPostData()返回true就执行RaisePostDataChangedEvent(),否则不引发。实现这个接口要注意的一点,就是必须把其中一个事件回发元素的name属性值设为这个控件的UniqueID,这样做很容易理解,就象上面那个LinkButton传递的参数一样,这个发回服务器端的UniqueID值使页框架能够确认这些数据是哪个控件发回来的。这个方法是标准的form回发,就象我们用asp时,一个表单中有一大堆的文本框、下拉框或者单选复选框,点击Submit按钮就将所有的这些表单元素的name属性值和它们的数据或状态发回服务器端,在asp.net中,这些回发的数据就是一个NameValueCollection对象,即一系列键、值对的集合。前面已经说过了,这其中必须有一个键名是这个引发回发的控件的UniqueID,否则页框架就分不清是哪个控件引发了回发事件以及回发的数据应该交给哪个控件去处理,这是很多编写自定义控件的网友最容易犯的错误。页框架在接收到的回发数据中如果发现某个键名是页面上某个控件的UniqueID,就知道这些数据是这个控件回发的,然后检查该控件是否需要自己处理回发的数据(即是否实现IPostBackDataHandler接口),如果需要自己处理,页框架就会把该控件的标识和回发的数据作为参数传递给IPostBackDataHandler接口的LoadPostData()方法去处理。
a name="log1lblSubmit" href="javascript:{if(ControlCheckValue())__doPostBack('log1','log1_submit')}" class="mainlink">登录</a>
我们要获得的是这个参数:“log1_submit”,此参数规定了这个按钮与其他按钮提交不同的事件,所以在定义IPostBackEventHandler和IPostBackDataHandler接口以前,必须定义此按钮的参数:
pbulic seals ButtonClickEventArgs : EventArgs
{
//定义存放log1_submit的变量
private string _ButtonArgs;
//构造函数,获得参数
public ButtonClickEnentArgs(string Args)
{
_ButtonArgs = Args;
} //返回获得的参数
public string ButtonArgs()
{
get
{
return _ButtonArgs;
}
}
}
然后,我们就可以定义上面两个接口,进行数据处理了,下面处理IPostBackEventHandler接口定义的方法
//获得参数并传递
public void RaisePostBackEvent(string args)
{
//此方法实际上是一个传送器,方法的参数
//这个args就是从asp.net框架获得的log1_submit参数,但是我们对他无法进行处理
//必须把它传递出去,这就利用了上面定义的ButtonClickEnentArgs
//我们传递给一个专门的OnButtonClick方法,进行处理
OnButtonClick(new ButtonClickEnentArgs(args));
}
下面是处理控件状态的,无内容,必须定义
public virtual void RaisePostDataChangedEvent(){}public virtual bool LoadPostData(string pkey,NameValueCollection pcol){}
下面我们该定义OnButtonClick()方法,用来判断控件的按钮,因为一个控件可能不止一个按钮,如果有两个以上,这里可以判断是哪个按钮回送
protected void OnButtonClick(ButtonClickEnentArgs e)
{
//获得控件名
string strID = this.UniqueID;
//然后判断传递过来的参数
switch (e.ButtonArgs)
{
case (strID + "_submit")
//进行处理,如调用代码,登录服务器等,我使用的是获得用户输入的密码
//放入_pass变量
_pass = Page.Request.Form.Item(this.UniqueID + "_pass");
break;
case ( /* 其他按钮 */)
//处理
break;
}
}这样,我们就可以实现在服务器控件里面获得用户输入,用户选择,等事件回发处理了。