引言
本篇文章将会讲述事件的定义以及其使用方式,并以ASP.NET的用户控件为例子,介绍一下自定义事件的使用。
目录
四、深入解析事件
4.1 事件的由来
在介绍事件之前大家可以先看看下面的例子, PriceManager 负责对商品价格进行处理,当委托对象 GetPriceHandler 的返回值大于100元,按8.8折计算,低于100元按原价计算。
1 public delegate double PriceHandler(); 2 3 public class PriceManager 4 { 5 public PriceHandler GetPriceHandler; 6 7 //委托处理,当价格高于100元按8.8折计算,其他按原价计算
8 public double GetPrice() 9 { 10 if (GetPriceHandler.GetInvocationList().Count() > 0) 11 { 12 if (GetPriceHandler() > 100) 13 return GetPriceHandler()*0.88; 14 else 15 return GetPriceHandler(); 16 } 17 return -1; 18 } 19 } 20 21 class Program 22 { 23 static void Main(string[] args) 24 { 25 PriceManager priceManager = new PriceManager(); 26 27 //调用priceManager的GetPrice方法获取价格
28 //直接调用委托的Invoke获取价格,两者进行比较
29 priceManager.GetPriceHandler = new PriceHandler(ComputerPrice); 30 Console.WriteLine(string.Format("GetPrice\n Computer's price is {0}!", 31 priceManager.GetPrice())); 32 Console.WriteLine(string.Format("Invoke\n Computer's price is {0}!", 33 priceManager.GetPriceHandler.Invoke())); 34 35 Console.WriteLine(); 36 37 priceManager.GetPriceHandler = new PriceHandler(BookPrice); 38 Console.WriteLine(string.Format("GetPrice\n Book's price is {0}!", 39 priceManager.GetPrice())); 40 Console.WriteLine(string.Format("Invoke\n Book's price is {0}!" , 41 priceManager.GetPriceHandler.Invoke())); 42 43 Console.ReadKey(); 44 } 45 //书本价格为98元
46 public static double BookPrice() 47 { 48 return 98.0; 49 } 50 //计算机价格为8800元
51 public static double ComputerPrice() 52 { 53 return 8800.0; 54 } 55 }
运行结果
观察运行的结果,如果把委托对象 GetPriceHandler 设置为 public ,外界可以直接调用 GetPriceHandler.Invoke 获取运行结果而移除了 GetPrice 方法的处理,这正是开发人员最不想看到的。
为了保证系统的封装性,开发往往需要把委托对象 GetPriceHandler 设置为 private, 再分别加入 AddHandler,RemoveHandler 方法对 GetPriceHandler 委托对象进行封装。
1 public delegate double PriceHandler(); 2 3 public class PriceManager 4 { 5 private PriceHandler GetPriceHandler; 6 7 //委托处理,当价格高于100元按8.8折计算,其他按原价计算
8 public double GetPrice() 9 { 10 if (GetPriceHandler!=null) 11 { 12 if (GetPriceHandler() > 100) 13 return GetPriceHandler()*0.88; 14 else 15 return GetPriceHandler(); 16 } 17 return -1; 18 } 19 20 public void AddHandler(PriceHandler handler) 21 { 22 GetPriceHandler += handler; 23 } 24 25 public void RemoveHandler(PriceHandler handler) 26 { 27 GetPriceHandler -= handler; 28 } 29 } 30 ................ 31 ................
为了保存封装性,很多操作都需要加入AddHandler、RemoveHandler 这些相似的方法代码,这未免令人感到厌烦。
为了进一步简化操作,事件这个概念应运而生。
4.2 事件的定义
事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX 两个方法,用作注册与注销事件的处理方法。而且事件对应的变量成员将会被视为 private 变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐的代码。
1 public class EventTest 2 { 3 public delegate void MyDelegate(); 4 public event MyDelegate MyEvent; 5 }
观察事件的编译过程可知,在编译的时候,系统为 MyEvent 事件自动建立add_MyEvent、remove_MyEvent 方法。
4.3 事件的使用方式
事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 add_XXX、remove_XXX 进行处理。
值得留意,在PersonManager类的Execute方法中,如果 MyEvent 绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变量成员。
注意:在事件所处的对象之外,事件只能出现在+=,-=的左方。
此时,开发人员无须手动添加 add_XXX、remove_XXX 的方法,就可实现与4.1例子中的相同功能,实现了良好的封装。
1 public delegate void MyDelegate(string name); 2 3 public class PersonManager 4 { 5 public event MyDelegate MyEvent; 6 7 //执行事件
8 public void Execute(string name) 9 { 10 if (MyEvent != null) 11 MyEvent(name); 12 } 13 } 14 15 class Program 16 { 17 static void Main(string[] args) 18 { 19 PersonManager personManager = new PersonManager(); 20 //绑定事件处理方法
21 personManager.MyEvent += new MyDelegate(GetName); 22 personManager.Execute("Leslie"); 23 Console.ReadKey(); 24 } 25 26 public static void GetName(string name) 27 { 28 Console.WriteLine("My name is " + name); 29 } 30 }
4.4 事件处理方法的绑定
在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。
1 public delegate void MyDelegate(string name); 2 3 public class PersonManager 4 { 5 public event MyDelegate MyEvent; 6 ......... 7 } 8 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 PersonManager personManager = new PersonManager(); 14 //绑定事件处理方法
15 personManager.MyEvent += GetName; 16 ............. 17 } 18 19 public static void GetName(string name) 20 {.........} 21 }
如果觉得编写 GetName 方法过于麻烦,你还可以使用匿名方法绑定事件的处理。
1 public delegate void MyDelegate(string name); 2 3 public class PersonManager 4 { 5 public event MyDelegate MyEvent; 6 7 //执行事件
8 public void Execute(string name) 9 { 10 if (MyEvent != null) 11 MyEvent(name); 12 } 13 14 static void Main(string[] args) 15 { 16 PersonManager personManager = new PersonManager(); 17 //使用匿名方法绑定事件的处理
18 personManager.MyEvent += delegate(string name){ 19 Console.WriteLine("My name is "+name); 20 }; 21 personManager.Execute("Leslie"); 22 Console.ReadKey(); 23 } 24 }
4.5 C#控件中的事件
在C#控件中存在多个的事件,像Click、TextChanged、SelectIndexChanged 等等,很多都是通过 EventHandler 委托绑定事件的处理方法的,EventHandler 可说是C#控件中最常见的委托 。
public delegate void EventHandler (Object sender, EventArgs e)
EventHandler 委托并无返回值,sender 代表引发事件的控件对象,e 代表由该事件生成的数据 。在ASP.NET中可以直接通过btn.Click+=new EventHandler(btn_ 的方式为控件绑定处理方法。
1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head runat="server"> 3 <title></title> 4 <script type="text/C#" runat="server">
5 protected void Page_Load(object sender, EventArgs e)
6 {
7 btn.Click += new EventHandler(btn_onclick);
8 }
9
10 public void btn_ obj, EventArgs e)
11 {
12 Button btn = (Button)obj;
13 Response.Write(btn.Text);
14 }
15 </script> 16 </head> 17 <body> 18 <form id="form1" runat="server"> 19 <div> 20 <asp:Button ID="btn" runat="server" Text="Button"/> 21 </div> 22 </form> 23 </body> 24 </html>
更多时候,只需要在页面使用 OnClick=“btn_ 方法,在编译的时候系统就会自动对事件处理方法进行绑定。
1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head runat="server"> 3 <title></title> 4 <script type="text/C#" runat="server">
5 public void btn_ obj, EventArgs e)
6 {
7 Button btn = (Button)obj;
8 Response.Write(btn.Text);
9 }
10 </script> 11 </head> 12 <body> 13 <form id="form1" runat="server"> 14 <div> 15 <asp:Button ID="btn" runat="server" Text="Button" OnClick="btn_onclick"/> 16 </div> 17 </form> 18 </body> 19 </html>
EventHandler 只是 EventHandler<TEventArgs> 泛型委托的一个简单例子。事实上,大家可以利用 EventHandler<TEventArgs> 构造出所需要的委托。
public delegate void EventHandler<TEventArgs> (Object sender, TEventArgs e)
在EventHandler<TEventArgs>中,sender代表事件源,e 代表派生自EventArgs类的事件参数。开发人员可以建立派生自EventArgs的类,从中加入需要使用到的事件参数,然后建立 EventHandler<TEventArgs> 委托。
下面的例子中,先建立一个派生自EventArgs的类MyEventArgs作为事件参数,然后在EventManager中建立事件myEvent , 通过 Execute 方法可以激发事件。最后在测试中绑定 myEvent 的处理方法 ShowMessage,在ShowMessage显示myEventArgs 的事件参数 Message。
1 public class MyEventArgs : EventArgs 2 { 3 private string args; 4 5 public MyEventArgs(string message) 6 { 7 args = message; 8 } 9 10 public string Message 11 { 12 get { return args; } 13 set { args = value; } 14 } 15 } 16 17 public class EventManager 18 { 19 public event EventHandler<MyEventArgs> myEvent; 20 21 public void Execute(string message) 22 { 23 if (myEvent != null) 24 myEvent(this, new MyEventArgs(message)); 25 } 26 } 27 28 class Program 29 { 30 static void Main(string[] args) 31 { 32 EventManager eventManager = new EventManager(); 33 eventManager.myEvent += new EventHandler<MyEventArgs>(ShowMessage); 34 eventManager.Execute("How are you!"); 35 Console.ReadKey(); 36 } 37 38 public static void ShowMessage(object obj,MyEventArgs e) 39 { 40 Console.WriteLine(e.Message); 41 } 42 }
运行结果
4.6 为用户控件建立事件
在ASP.NET开发中,页面往往会出现很多类似的控件与代码,开发人员可以通过用户控件来避免重复的代码。但往往同一个用户控件,在不同的页面中需要有不同的响应。此时为用户控件建立事件,便可轻松地解决此问题。
下面例子中,在用户控件 MyControl 中建立存在一个GridView控件,GridView 控件通过 GetPersonList 方法获取数据源。在用户控件中还定义了 RowCommand 事件,在 GridView 的 GridView_RowCommand 方法中激发此事件。这样,在页面使用此控件时,开发人员就可以定义不同的方法处理 RowCommand 事件。
1 public class Person 2 { 3 public int ID 4 { get; set; } 5 public string Name 6 { get; set; } 7 public int Age 8 { get; set; } 9 } 10 11 <!-- 用户控件 --> 12 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="MyControl" %> 13 <script type="text/C#" runat="server">
14 protected void Page_Load(object sender, EventArgs e)
15 {
16 GridView1.DataSource = GetPersonList();
17 GridView1.DataBind();
18 }
19
20 //绑定数据源
21 protected IList<Person> GetPersonList()
22 {
23 IList<Person> list = new List<Person>();
24 Person person1 = new Person();
25 person1.ID = 1;
26 person1.Name = "Leslie";
27 person1.Age = 29;
28 list.Add(person1);
29 ...........
30 return list;
31 }
32
33 public event GridViewCommandEventHandler RowCommand;
34
35 protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
36 {
37 if (RowCommand != null)
38 RowCommand(sender, e);
39 }
40 </script> 41 <div> 42 <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
43 onrowcommand="GridView1_RowCommand"> 44 <Columns> 45 <asp:BoundField DataField="ID" HeaderText="ID"/> 46 <asp:BoundField DataField="Name" HeaderText="Name"/> 47 <asp:BoundField DataField="Age" HeaderText="Age"/> 48 <asp:ButtonField CommandName="Get" Text="Select"/> 49 </Columns> 50 </asp:GridView> 51 </div> 52 53 <!-- 页面代码 --> 54 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> 55 <%@ Register Src="~/MyControl.ascx" TagPrefix="ascx" TagName="myControl" %> 56 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 57 58 <html xmlns="http://www.w3.org/1999/xhtml"> 59 <head runat="server"> 60 <title></title> 61 <script type="text/C#" runat="server">
62 protected void myControl_RowCommand(object sender, GridViewCommandEventArgs e)
63 {
64 if (e.CommandName == "Get")
65 {
66 GridView gridView=(GridView)sender;
67 int index = int.Parse(e.CommandArgument.ToString());
68 label.Text=gridView.Rows[index].Cells[1].Text;
69 }
70 }
71 </script> 72 </head> 73 <body> 74 <form id="form1" runat="server"> 75 <div> 76 <ascx:myControl ID="myControl" runat="server" OnRowCommand="myControl_RowCommand"></ascx:myControl> 77 <br /> 78 Select Name : <asp:Label ID="label" runat="server"></asp:Label><br /> 79 </div> 80 </form> 81 </body> 82 </html>
运行结果
使用控件已有的事件固然简单,但它限制了传送的参数类型,使开发人员无法传送额外的自定义参数。在结构比较复杂的用户控件中,使用已有的控件事件,显然不够方便,此时,您可以考虑为用户控件建立自定义事件。
首先用户控件中包含订单信息与订单明细列表,首先定义一个事件参数 MyEventArgs,里面包含了订单信息与一个 OrderItem 数组。然后建立用户控件的委托MyDelegate 与对应的事件 MyEvent,在 Button 的 Click 事件中激发 MyEvent 自定义事件。这样在页面处理方法 myControl_Click 中就可以通过事件参数 MyEventArgs 获取用户控件中的属性,计算订单的总体价格。
1 <!-- 基础类 --> 2 public class OrderItem 3 { 4 public OrderItem(string id,string goods,double price,int count) 5 { 6 this.OrderItemID = id; //明细单ID 7 this.Goods = goods; //商品名称 8 this.Price = price; //商品单价 9 this.Count = count; //商品数量 10 } 11 12 public string OrderItemID 13 { get; set; } 14 public string Goods 15 { get; set; } 16 public double Price 17 { get; set; } 18 public int Count 19 { get; set; } 20 } 21 22 /// 事件参数 23 public class MyEventArgs:EventArgs 24 { 25 public MyEventArgs(string name,string address,string tel, 26 string orderCode,IList<OrderItem> orderItemList) 27 { 28 Name = name; //买家姓名 29 Address = address; //买家地址 30 Tel = tel; //买家电话 31 OrderCode = orderCode; //订单号码 32 OrderItemList = orderItemList; //订单明细 33 } 34 35 public string Name 36 { get;set; } 37 public string Address 38 { get; set; } 39 public string Tel 40 { get; set; } 41 public string OrderCode 42 { get; set; } 43 public IList<OrderItem> OrderItemList 44 { get; set; } 45 } 46 47 <!-- 用户控件 --> 48 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="MyControl" %> 49 <script type="text/C#" runat="server">
50 protected void Page_Load(object sender, EventArgs e)
51 {
52 GridView1.DataSource = GetList();
53 GridView1.DataBind();
54 }
55
56 //模拟数据源
57 protected IList<OrderItem> GetList()
58 {
59 IList<OrderItem> list = new List<OrderItem>();
60 OrderItem orderItem = new OrderItem("1", "Asus N75S", 8800, 2);
61 list.Add(orderItem);
62 ..........
63 return list;
64 }
65
66 //自定义委托
67 public delegate void MyDelegate(object sender,MyEventArgs myEventArgs);
68 //自定义事件
69 public event MyDelegate MyEvent;
70
71 //按下Button时激发自定义事件
72 protected void btn_click(object sender, EventArgs e)
73 {
74 if (MyEvent != null)
75 {
76 MyEventArgs myEventArgs = new MyEventArgs(labelName.Text, labelAddress.Text, labelTel.Text
77 , labelOrderCode.Text, GetList());
78 MyEvent(this,myEventArgs);
79 }
80 }
81 </script> 82 <div> 83 Name : <asp:Label ID="labelName" runat="server">Leslie</asp:Label><br /> 84 Address : <asp:Label ID="labelAddress" runat="server">ZhongShan University 2A 501</asp:Label><br /> 85 Tel : <asp:Label ID="labelTel" runat="server">13660123456</asp:Label><br /> 86 Order Code : <asp:Label ID="labelOrderCode" runat="server">A12012031223B0030</asp:Label><br /><br /> 87 <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="5"> 88 <Columns> 89 <asp:BoundField DataField="OrderItemID" HeaderText="ID"/> 90 <asp:BoundField DataField="Goods" HeaderText="Goods"/> 91 <asp:BoundField DataField="Price" HeaderText="Price"/> 92 <asp:BoundField DataField="Count" HeaderText="Count"/> 93 </Columns> 94 </asp:GridView> 95 <br /> 96 <asp:Button ID="btn" runat="server" Text="Account" OnClick="btn_click"/> 97 </div> 98 99 <!-- 页面处理 --> 100 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> 101 <%@ Register Src="~/MyControl.ascx" TagPrefix="ascx" TagName="myControl" %> 102 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 103 104 <html xmlns="http://www.w3.org/1999/xhtml"> 105 <head runat="server"> 106 <title></title> 107 <script type="text/C#" runat="server">
108 //在页面定义用户控件MyEvent事件的处理方法
109 protected void myControl_Click(object sender,MyEventArgs e)
110 {
111 //计算订单总体价格
112 double totalPrice=0;
113 IList<OrderItem> list=e.OrderItemList;
114 foreach(OrderItem item in list)
115 totalPrice+=item.Price*item.Count;
116 //展示订单号及总体费用
117 labelOrderCode.Text = e.OrderCode;
118 labelTotalPrice.Text = totalPrice.ToString();
119 }
120 </script> 121 </head> 122 <body> 123 <form id="form1" runat="server"> 124 <div> 125 <ascx:myControl ID="myControl" runat="server" OnMyEvent="myControl_Click"></ascx:myControl> 126 <br /> 127 OrderCode : <asp:Label ID="labelOrderCode" runat="server"></asp:Label><br /> 128 TotalPrice : <asp:Label ID="labelTotalPrice" runat="server"></asp:Label> 129 </div> 130 </form> 131 </body> 132 </html>
运行结果
若对自定义事件不太熟悉的朋友很多时候会使用 UserControl.FindControl 的方式获取用户控件中的属性,但当你深入了解自定义事件的开发过程以后,就能有效简化开发的过程。
转载于:https://blog.51cto.com/79100812/843499