亡灵形态仍在产生事件

Undead form is still generating events

我有一个用户可以从另一个表单打开的表单:

  private void btnEditTemplate_Click(object sender, EventArgs e)
  {
     using (frmReport EditReport = new frmReport()) 
     {
        EditReport.ShowDialog();
        EditReport.Close(); 
        EditReport.Dispose();
     }
  }

如您所见,我调用了关闭、处置,甚至将表单放入了 using 块中。表单有一些控件绑定到在表单外部维护的静态 BindingLists,更改这些列表会触发事件。

尽管如此,如果用户稍后打开此表单的另一个副本,我发现原始表单的事件仍在 运行,即使它已关闭和处置。

我需要做什么才能杀死这个东西?穿过心脏的木桩?

更新: 正如评论中指出的那样,问题在于静态 BindingList 对象正在维护与旧形式的数据绑定。对我来说,解决方案是摆脱 BindingList 对象,因为无论如何我都没有(有意)使用它们的数据绑定属性。

一般来说,我发现编写事件订阅代码以容忍在取消订阅事件后被调用是一种很好的做法。这可以通过多种方式发生。

我不知道你做了什么实验,但是例如,如果你这样写:

private void btnEditTemplate_Click(object sender, EventArgs e)
{
  using (frmReport EditReport = new frmReport()) 
  {
    EditReport.ShowDialog();
    EditReport.Close(); 
    EditReport.Dispose();
  }
  using (frmReport EditReport2 = new frmReport()) 
  {
    EditReport2.ShowDialog();
    EditReport2.Close(); 
    EditReport2.Dispose();
  }
}

那么第一个对话框的消息队列中可能仍有事件不会得到处理,直到您在第二个实例的 ShowDialog() 调用中进入模态消息循环。

即使使用您拥有的 Dispose(),垃圾收集器也不会回收第一个对象,因为在消息队列中的这些订阅中对它的引用。如果你编写你的事件订阅,如果它们被传递到的对象已经被释放,则什么也不做,那么就不会造成任何伤害。要检查的内容因您使用的基础 class 而异。最坏的情况是,您实现自己的 IsDisposed 或 IsClosed 实例变量。

暂时忽略事件处理程序和数据绑定,您的代码目前有点矫枉过正。你可以这样做:

using (frmReport EditReport = new frmReport())
{
    EditReport.ShowDialog();
}

当它到达您的 .Close() 电话时,您将其显示为对话框,无论如何,该表单已关闭。 using 语句为您调用 .Dispose()

现在,当您处理一个表单时,您基本上是从其父表单中删除该表单(如果有的话),处理所有子控件,并释放 Windows 句柄。

但是,它不知道如何分离您的事件处理程序,也不知道如何删除数据绑定。由于这些东西是对您的表单的引用,因此垃圾收集器会将表单视为活动对象并且不会尝试收集它。

因此,如果您的表单中有任何处理程序侦听外部事件并且它们触发,那么您的表单将尝试处理它们。

您需要确保在关闭表单时分离所有处理程序并取消绑定所有数据绑定。

第二次打开表单与第一次没有直接关系,但它很可能导致您的数据源开始引发事件或更新值,而这正是您的原始表单出错的原因。