【示例】
有父窗体A,和两个MDI子窗体一个叫B,一个叫C
左边的窗体是子窗体B,右边的窗体是窗体C
B读xml文件,读到轨道信息则给窗体C显示
因为是MDI子窗体,所以同时拥有BC窗体的只有主窗体A
所以B想要触发C窗体的事件只能通过A,因此窗体A是事件拥有者(PS:其实窗体B才是真实的事件拥有者)
代码部分
FormA的代码
//FormA里的代码
public event Action<DataTable> OnDataTable;
public void TriggerDataTable(DataTable dt)
{
if (dt != null)
{
OnDataTable(dt);
}
}
FormB代码
//FormB
//此TreeForm添加的打开文件操作,即找到轨道地理信息文件则窗体A的TriggerData方法
private void btn_OpenFile_ItemClick(object sender, ItemClickEventArgs e)
{
OpenFileDialog o = new OpenFileDialog();
o.InitialDirectory = Application.StartupPath;
o.Title = "打开文件";
o.Multiselect = false;
o.RestoreDirectory = true;
o.Filter = "Xml文件(*.xml)|*.xml";
o.Multiselect = true;
if (o.ShowDialog() == DialogResult.OK)
{
string[] xmlFile = o.FileNames;
ShowTree(xmlFile);
//找到这组文件中的轨道地理信息文件
string trackGeoFile = OnCertainFile(xmlFile,"Track");
if(trackGeoFile!=null)
{
(this.ParentForm as Form1).TriggerDataTable(GetTracksDt(trackGeoFile));
}
}
}
FormC的代码
public void TrackGeoInfo_OnDataTable(DataTable dt)
{
this.gridControl1.DataSource = null;
this.gridControl1.DataSource = dt;
//this.gridControl1.DataSource = dts[1];
//
this.gridControl1.RefreshDataSource();
}
//窗体CLoad则订阅事件
private void TrackGeoInfo_Load(object sender, EventArgs e)
{
try
{
this.Dock = DockStyle.Fill;
parentForm = (Form1)this.MdiParent;
(this.ParentForm as Form1).OnDataTable += new Action<DataTable>(TrackGeoInfo_OnDataTable);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void TrackGeoInfo_FormClosed(object sender, FormClosedEventArgs e)
{
(this.ParentForm as Form1).OnDataTable -= new Action<DataTable>(TrackGeoInfo_OnDataTable);
}
【事件的组成】
一个人开枪,一群鸟听到枪声飞跑。
事件的组成:
(1)事件的拥有者-------发起事件的对象---人
(2)事件本身----开枪
(3)事件的订阅者---关注事件的对象---这群鸟
(4)事件处理器-----订阅者在关注到事件发生后,针对事件作出的反应----“飞走”是这群鸟针对“开枪”事件做出的处理
(5)订阅事件-------一种关系,联系拥有者和订阅者、事件和事件处理
【事件的使用】
定义事件—event
事件定义是需要发配委托:修饰符+event+委托类型+名称
比如:public event Action Fire;//即开枪事件
事件的触发只能在被声明该事件的类中进行,即人这类
public class Man//事件拥有者-人
{
//此处使用执行无参数无返回值方法的委托类型
public event Action Fire;//事件本身-开枪
//封装了事件的类方法
public void Action()
{
if(Fire!=null)
{
Console.WriteLine("人开枪了");
//触发事件
Fire();
}
}
}
//鸟----事件订阅者
public class Bird
{
public string birdName { get; set; }
//声明一个集合类型的静态字段,用于保存实例化的所有Bird类
public static List<Bird> Birds = new List<Bird>();
//这是鸟类对于开枪的事件处理器
internal static void Fly()
//internal 关键字是类型和类型的成员 访问修饰符。只有在同一程序集的文件中,内部类型或成员才是可访问的
{
for (int i = 0; i < Birds.Count; i++)
{
Console.WriteLine(Birds[i].birdName + "飞走了");
}
}
//每一次实例化都会在构造函数中保存当前实例化的Bird类
public Bird()
{
Birds.Add(this);
}
}
class Program
{
static void Main(string[] args)
{
//实例化对象
Man man = new Man();
//订阅事件,联结两类对象中的事件和事件处理器
man.Fire += Bird.Fly;
//循环生成Bird类
for (int i = 0; i < 5; i++)
{
Bird bird = new Bird();
bird.birdName = "小鸟" + i;
}
//调用封装事件的方法,触发事件
man.Action();
}
}
[思考]
因此知道
在事件拥有者—人这一类里写入事件声明,事件的方法Fire()
在事件订阅者里写上订阅事件,事件处理器Fly()
man.Fire += Bird.Fly;
这句话即事件拥有者Man,发起事件Fire(),事件订阅者Bird鸟类,订阅事件+=,事件处理器飞走Fly()
所以说订阅事件就是事件拥有者、事件、事件订阅者、事件处理器之间的联系
【分析示例的事件】
【FormA(Form1)-事件的拥有者:主窗体】
事件的拥有者:主窗体
//Action表示无参数无返回值的委托
//Action
public event Action
//(主窗体的)事件-主窗体得到子窗体B的轨道地理信息文件DataTable
public void TriggerDataTable(DataTable dt)
{
if (dt != null)
{
OnDataTable(dt);
}
}
【FormB(TreeForm)-事件真实拥有者:树文件窗体】
因为窗体B不拥有窗体C,且他是隐藏的事件拥有者,他为了要发起事件所以要在打开文件的事件里写上窗体A的事件本身
(this.ParentForm as Form1).TriggerDataTable(GetTracksDt(trackGeoFile));
窗体B是实际让事件发生的人??
【FormC(TrackGeoInfo)—事件订阅者:轨道信息窗体】
事件订阅者,需要订阅事件,还有类似鸟-飞走这样的事件处理
在窗体C的Load事件里写订阅,所以发现他们的订阅都是通过窗体A的OnDataTable事件
(this.ParentForm as Form1).OnDataTable += new Action
public void TrackGeoInfo_OnDataTable(DataTable dt)
{
this.gridControl1.DataSource = null;
this.gridControl1.DataSource = dt;
this.gridControl1.RefreshDataSource();
}
在窗体C的窗体关闭事件里写取消订阅事件即-=