上篇文章我们初探了事件,了解了事件的构成、声明和使用,但是我们还是不明白Winform里类似下面的的事件代码到底是什么意思。
private void button1_Click(object sender, EventArgs e)
{
}
WinForm编程是事件驱动的,在类似于按钮点击、按下按键或文本框内容改变等事件发生时,程序要处理完事件然后继续其他事情。
正是因为在WinForm中事件使用的如此广泛,为了更方便、更规范的使用事件,.NET框架提供了一个标准模式。
标准事件不再使用用户自已声明的委托了,而是使用.NET框架在System命名空间中声明的一个EventHandler委托类型。
说白了就是系统已经替我们声明好了一个委托类型,就省的我们每次声明事件时,再去多声明一次委托了。
这个EventHandler在系统中声明的是这个样子的:
public delegate void EventHandler(object sender,EventArgs e);
注意:这个委托类型,系统已经帮我们声明好了,我们不需要重复声明,也不能修改这个声明。
这个委托类型没有返回值,值的注意的是它的两个参数:
第一个参数用来保存触发事件的对象的引用。由于是object类型的,所以可以匹配任何类型的实例。
第二个参数用来保存状态信息,指明什么类型适用于该应用程序。(《C#图解教程》第4版P261)
理解了这一点,我们不应该再用我们在上一篇文章里声明的具有3个参数的PayDel委托类型了。
一、标准事件的用法
看看我修改好的代码
using System;
namespace event_1
{
//发布者:银行卡类
class BankCard
{
public string Name { set; get; } //银行卡的名称
public int Balance { set; get; } //银行卡的余额
public event EventHandler PayEvent; //声明刷卡事件,使用EventHandler委托
public void PayByCard(int amount)
{
this.Balance -= amount;
if (PayEvent != null)
{
PayEvent(this, null); //触发事件
}
}
}
//订阅者:手机类
class Phone
{
public void Display(object sender, EventArgs e) //回调方法,参数必须和EventHandler委托一致
{
Console.WriteLine("短信:您的银行卡有一笔消费,如非本人操作,请立刻致电本行。");
}
}
class Program
{
static void Main(string[] args)
{
BankCard ICBC = new BankCard { Name = "工商银行", Balance = 5000 }; //实例化一个银行卡 工商银行,余额5000元
Phone IPhone = new Phone(); //实例化一个手机 我的手机
ICBC.PayEvent += IPhone.Display; //把订阅者(手机)的事件处理方法(显示)绑订到事件上。
ICBC.PayByCard(1000); //执行刷卡操作
}
}
}
看,我们“成功”地把刷卡短信通知程序按标准事件的模式改写了。但好像我们的程序又回到了最原始的状态--只是发了个通知,关于银行卡的名称、余额和本次刷卡金额,我们一无所知。
我们来研究一下EventHandler委托类型的两个参数object类型的 sender和EventArgs类型的e,第一个参数用来保存触发事件的对象的引用,第二个参数用于保存状态信息,那就是说第二个参数用于传递数据了?
错了!
EventArgs设计为不能传递任何数据。它用于不需要传递数据的事件处理程序。
事实上WinForm的很多控件事件就是不需要传递数据的。像按钮点击事件,一点就执行回调方法就行了,还要什么自行车。
但问题是,现在的程序设计需求,这个事件就是要传递数据啊,怎么办呢?
二、能传递数据的EventArgs
1、声明一个派生自EventArgs的自定义类
//自定义一个PayEventArgs类,它派生自EventArgs类
public class PayEventArgs : EventArgs
{
public int amount { get; set; } //声明一个Int类型的属性,来保存刷卡金额。
}
2、使用自定义类的委托类型来声明事件
public event EventHandler<PayEventArgs> PayEvent;//声明事件,使用自定义的泛型委托EventHandler<PayEventArgs>
3、使用自定义类和自定义委托来传数据
namespace event_1
{
//自定义一个PayEventArgs类,它派生自EventArgs类
public class PayEventArgs : EventArgs
{
public int amount { get; set; } //声明一个Int类型的属性,来保存刷卡金额。
}
//发布者:银行卡类
class BankCard
{
public string Name { set; get; }
public int Balance { set; get; }
public event EventHandler<PayEventArgs> PayEvent;//声明事件,使用自定义的泛型委托EventHandler<PayEventArgs>
public void PayByCard(int amount)
{
PayEventArgs args = new PayEventArgs(); //实例化自定义类PayEventArgs的对象args
this.Balance -= amount;
if (PayEvent != null)
{
args.amount = amount; //将刷卡金额赋值给args的amount属性
PayEvent(this, args); //触发事件时将传递两个参数,一个是this,也就是银行卡的实例的引用,
//另一个args内包含我们要传递的amount。
}
}
}
//订阅者:手机类
class Phone
{
public void Display(object sender, PayEventArgs e) //回调方法,注意第二个参数使用了我们自定义的派生类PayEventArgs
{
Console.WriteLine("短信:您的银行卡消费{0}元。", e.amount);//e.amount就是PayEventArgs实例的amount属性。
}
}
class Program
{
static void Main(string[] args)
{
BankCard ICBC = new BankCard { Name = "工商银行", Balance = 5000 };
Phone IPhone = new Phone();
ICBC.PayEvent += IPhone.Display; //绑订到事件
ICBC.PayByCard(1000); //执行刷卡操作
}
}
}
运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/67ea5968fc8ba62703d2c68a3d250894.jpeg)
为什么刷卡金额传过来了,银行卡的名称和余额却没有传过来?
EventHandler的第一个参数object类型的 sender,好像没有什么用?
三、Sender的作用
sender的意思是发送者,用来保存触发事件的对象的引用。说白了,就是发送事件的那个类(发布者)自己。
发布者知道了,,我们自然能取得发布者类的属性,那什么名称啦余额啦也就全知道了。
全部代码如下,其实比上一版就改动了一点点,自己找不同吧
namespace event_1
{
//自定义一个PayEventArgs类,它派生自EventArgs类
public class PayEventArgs : EventArgs
{
public int amount { get; set; } //声明一个Int类型的属性,来保存刷卡金额。
}
//发布者:银行卡类
class BankCard
{
public string Name { set; get; }
public int Balance { set; get; }
public event EventHandler<PayEventArgs> PayEvent;//声明事件,使用自定义的泛型委托EventHandler<PayEventArgs>
public void PayByCard(int amount)
{
PayEventArgs args = new PayEventArgs(); //实例化自定义类PayEventArgs的对象args
this.Balance -= amount;
if (PayEvent != null)
{
args.amount = amount; //将刷卡金额赋值给args的amount属性
PayEvent(this, args); //触发事件时将传递两个参数,一个是this,也就是银行卡的实例的引用,
//另一个args内包含我们要传递的amount。
}
}
}
//订阅者:手机类
class Phone
{
public void Display(object sender, PayEventArgs e) //回调方法,注意第二个参数使用了我们自定义的派生类PayEventArgs
{
BankCard card = (BankCard)sender; //将事件传过来的object类型的发送者“银行卡”强制转换为银行卡类
Console.WriteLine("短信:您的{0}银行卡消费{1}元,当前余额{2}元。", card.Name,e.amount, card.Balance);
//名称:card.Name,是从sender中取得的,余额:e.amount是从e中取得的,当次刷卡金额是从sender中取得的。
}
}
class Program
{
static void Main(string[] args)
{
BankCard ICBC = new BankCard { Name = "工商银行", Balance = 5000 };
Phone IPhone = new Phone();
ICBC.PayEvent += IPhone.Display; //绑订到事件
ICBC.PayByCard(1000); //执行刷卡操作
}
}
}
运行结果:
好了,关于委托和事件我们就学习到这里,希望我的努力能帮助到您。