【示例】

有父窗体A,和两个MDI子窗体一个叫B,一个叫C
image.png
左边的窗体是子窗体B,右边的窗体是窗体C
B读xml文件,读到轨道信息则给窗体C显示

因为是MDI子窗体,所以同时拥有BC窗体的只有主窗体A
所以B想要触发C窗体的事件只能通过A,因此窗体A是事件拥有者(PS:其实窗体B才是真实的事件拥有者)

代码部分

FormA的代码

  1. //FormA里的代码
  2. public event Action<DataTable> OnDataTable;
  3. public void TriggerDataTable(DataTable dt)
  4. {
  5. if (dt != null)
  6. {
  7. OnDataTable(dt);
  8. }
  9. }

FormB代码

  1. //FormB
  2. //此TreeForm添加的打开文件操作,即找到轨道地理信息文件则窗体A的TriggerData方法
  3. private void btn_OpenFile_ItemClick(object sender, ItemClickEventArgs e)
  4. {
  5. OpenFileDialog o = new OpenFileDialog();
  6. o.InitialDirectory = Application.StartupPath;
  7. o.Title = "打开文件";
  8. o.Multiselect = false;
  9. o.RestoreDirectory = true;
  10. o.Filter = "Xml文件(*.xml)|*.xml";
  11. o.Multiselect = true;
  12. if (o.ShowDialog() == DialogResult.OK)
  13. {
  14. string[] xmlFile = o.FileNames;
  15. ShowTree(xmlFile);
  16. //找到这组文件中的轨道地理信息文件
  17. string trackGeoFile = OnCertainFile(xmlFile,"Track");
  18. if(trackGeoFile!=null)
  19. {
  20. (this.ParentForm as Form1).TriggerDataTable(GetTracksDt(trackGeoFile));
  21. }
  22. }
  23. }

FormC的代码

  1. public void TrackGeoInfo_OnDataTable(DataTable dt)
  2. {
  3. this.gridControl1.DataSource = null;
  4. this.gridControl1.DataSource = dt;
  5. //this.gridControl1.DataSource = dts[1];
  6. //
  7. this.gridControl1.RefreshDataSource();
  8. }
  9. //窗体CLoad则订阅事件
  10. private void TrackGeoInfo_Load(object sender, EventArgs e)
  11. {
  12. try
  13. {
  14. this.Dock = DockStyle.Fill;
  15. parentForm = (Form1)this.MdiParent;
  16. (this.ParentForm as Form1).OnDataTable += new Action<DataTable>(TrackGeoInfo_OnDataTable);
  17. }
  18. catch (Exception ex)
  19. {
  20. MessageBox.Show(ex.ToString());
  21. }
  22. }
  23. private void TrackGeoInfo_FormClosed(object sender, FormClosedEventArgs e)
  24. {
  25. (this.ParentForm as Form1).OnDataTable -= new Action<DataTable>(TrackGeoInfo_OnDataTable);
  26. }

【事件的组成】

  1. 一个人开枪,一群鸟听到枪声飞跑。
  2. 事件的组成:
  3. (1)事件的拥有者-------发起事件的对象---人
  4. (2)事件本身----开枪
  5. (3)事件的订阅者---关注事件的对象---这群鸟
  6. (4)事件处理器-----订阅者在关注到事件发生后,针对事件作出的反应----“飞走”是这群鸟针对“开枪”事件做出的处理
  7. (5)订阅事件-------一种关系,联系拥有者和订阅者、事件和事件处理

【事件的使用】

定义事件—event
事件定义是需要发配委托:修饰符+event+委托类型+名称
比如:public event Action Fire;//即开枪事件
事件的触发只能在被声明该事件的类中进行,即人这类

  1. public class Man//事件拥有者-人
  2. {
  3. //此处使用执行无参数无返回值方法的委托类型
  4. public event Action Fire//事件本身-开枪
  5. //封装了事件的类方法
  6. public void Action()
  7. {
  8. if(Fire!=null)
  9. {
  10. Console.WriteLine("人开枪了");
  11. //触发事件
  12. Fire();
  13. }
  14. }
  15. }
  16. //鸟----事件订阅者
  17. public class Bird
  18. {
  19.   public string birdName { get; set; }
  20.   //声明一个集合类型的静态字段,用于保存实例化的所有Bird类
  21.   public static List<Bird> Birds = new List<Bird>();
  22.   //这是鸟类对于开枪的事件处理器
  23.   internal static void Fly()
  24. //internal 关键字是类型和类型的成员 访问修饰符。只有在同一程序集的文件中,内部类型或成员才是可访问的
  25.   {
  26.     for (int i = 0; i < Birds.Count; i++)
  27.     {
  28.       Console.WriteLine(Birds[i].birdName + "飞走了");
  29.     }
  30.   }
  31.   //每一次实例化都会在构造函数中保存当前实例化的Bird类
  32.   public Bird()
  33.   {
  34.     Birds.Add(this);
  35.   }
  36. }
  37. class Program
  38. {
  39.   static void Main(string[] args)
  40.   {
  41.     //实例化对象
  42.     Man man = new Man();
  43.     //订阅事件,联结两类对象中的事件和事件处理器
  44.     man.Fire += Bird.Fly;
  45.     //循环生成Bird类
  46.     for (int i = 0; i < 5; i++)
  47.     {
  48.       Bird bird = new Bird();
  49.       bird.birdName = "小鸟" + i;
  50.     }
  51.     //调用封装事件的方法,触发事件
  52.     man.Action();
  53.   }
  54. }

[思考]

因此知道
在事件拥有者—人这一类里写入事件声明,事件的方法Fire()
在事件订阅者里写上订阅事件,事件处理器Fly()
man.Fire += Bird.Fly;
这句话即事件拥有者Man,发起事件Fire(),事件订阅者Bird鸟类,订阅事件+=,事件处理器飞走Fly()
所以说订阅事件就是事件拥有者、事件、事件订阅者、事件处理器之间的联系

【分析示例的事件】

【FormA(Form1)-事件的拥有者:主窗体】

事件的拥有者:主窗体
//Action表示无参数无返回值的委托
//Action表示有传入参数DataTable无返回值的委托
public event Action OnDataTable;

//(主窗体的)事件-主窗体得到子窗体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(TrackGeoInfo_OnDataTable);

public void TrackGeoInfo_OnDataTable(DataTable dt)
{
this.gridControl1.DataSource = null;
this.gridControl1.DataSource = dt;
this.gridControl1.RefreshDataSource();
}

在窗体C的窗体关闭事件里写取消订阅事件即-=