从Load处理程序关闭表单

时间:2009-04-08 17:51:53

标签: c# winforms objectdisposedexception

我有一种非常奇怪的行为,似乎只发生在一个表格上。

基本上我正在创建Form的实例,并调用Show()来显示非阻塞的表单。在该表单的Load事件处理程序中,我有一些逻辑可以在某些情况下调用this.Close()。这会关闭表单,但客户端代码中的表单Show()方法会抛出ObjectDisposedException

ObjectDisposedException的堆栈跟踪如下:

  

在System.Windows.Forms.Control.CreateHandle()
    在System.Windows.Forms.Form.CreateHandle()
    在System.Windows.Forms.Control.get_Handle()
    在System.Windows.Forms.ContainerControl.FocusActiveControlInternal()
    在System.Windows.Forms.Form.SetVisibleCore(布尔值)
    在System.Windows.Forms.Control.Show()
   ...等

这就是我所看到的:

  1. Control.Show()被称为
  2. 我的表单已启动
  3. 调用OnFormLoad方法
  4. 调用FormLoad事件处理程序,我在其中调用this.Close()
  5. 调用OnFormClosing方法
  6. 调用FormClosing事件处理程序
  7. Dispose在我的表单上调用,并且所有用户控件都被调用
  8. 然后在Control.Show()方法结束的某个地方,它会尝试获取表单的句柄,该表单会因为对象被标记为处置而变形并抛出异常。

    我真正的问题是,为什么我可以在我没有例外的其他形式上完成同样的事情?这是GC问题吗?我已经尝试在GC.Collect()之后立即拨打this.Close()电话,这没有任何区别。就像我说的那样,无论子用户控件,表单变量的范围等等,它都会在此表单上100%的时间内发生,而且绝不会在其他任何地方发生。

    有什么想法吗?

13 个答案:

答案 0 :(得分:25)

最好的方法:

 this.BeginInvoke(new MethodInvoker(this.Close));

这是最简单的方法,你不会得到ObjectDisposedException

答案 1 :(得分:8)

我知道这是一个老问题,但似乎没有人发布了obvoius答案。

您说您拨打Control.Show(),然后拨打Form.Close(),然后表单就是Disposed of。好吧,除非您使用MDI或使用与文档一样的ShowDialog。虽然,Close()文档的简短版本是“关闭表单”,但它实际上也在某些条件下隐式处理它。

请参阅备注部分: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.close.aspx

如果您想再次显示表单。使用Hide()方法代替Close()

希望能帮助其他寻找灵魂。

伙计们,不要停止搜索“我不知道为什么它有时会起作用”。这就变成了有很多防御性的错误软件“我会再次调用这种方法以防万一”。不好。

答案 2 :(得分:6)

好的,不想回答我自己的问题,但这让我疯了,这是我见过的最难复制的错误之一。

在我的表单上,我重写了OnFormLoad和OnFormClose方法,我将表单的大小,位置和WindowState保存/恢复到注册表中。我把这个代码拿出来解决了问题。奇怪的是,我把它放回去,问题没有回来。

我终于重现了这个问题:你必须让表单完全打开,最大化它,然后关闭它,以便将Maximized状态保存到注册表中。然后当你再次打开它时,它会将它设置为Maximized,如果它在Load处理程序中关闭,它会在它关闭时尝试访问Size / Location。显然,在OnFormClosing方法中访问这些值会导致表单尝试聚焦IF并且仅在表单最大化时才会聚焦,这是非法的,因为表单已被处理。

所以基本上,你不能在窗体的OnFormClosing方法中访问Form显示属性,如果那个窗体要从它的Load事件调用Close。(除非你先检查Disposed prop)

我知道的Winforms智慧非常具体,但无论如何我都是写下来的。

答案 3 :(得分:4)

如果要关闭表单,就像用户按下右上角的十字一样(通常意味着取消),只需添加以下代码即可。

this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Close();

这也适用于表单加载功能:

private void MyForm_Load (object sender, EventArgs e)
{
    // do some initializations

    if (!ContinueLoadingForm())
    {
         this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
         this.Close();
         return;
    }
    // continue loading the form
}

如果您不希望表单在短时间内可见,请将Visible属性设置为false(例如在设计器或构造函数中),并在确定程序可以继续加载时将其设置为true。

答案 4 :(得分:3)

在load事件中关闭表单并不是一个好主意。在Activated事件之后执行此操作。

答案 5 :(得分:2)

set_object_terms

答案 6 :(得分:1)

一种可能性:

他们可能在此表单上有一个计时器,正在FormLoad事件中初始化并启用它。如果计时器试图在表单被触发时尝试访问该表单,则需要在表单关闭之前禁用和停止计时器。

在此之前我见过表格......

答案 7 :(得分:0)

在我看来,如果不仔细研究它,最简洁的方法就是创建一个自定义的表单类来完成你想要的,可以创建Form,并覆盖OnFormLoad(...)和/或{ {1}}检查您的情况并尽早取消。

那就是说,我不知道为什么它有时会起作用,而不是其他时候。

答案 8 :(得分:0)

您是否曾尝试单步执行.net代码以查看发生异常时调用的代码行?如果您有VS 2008,可以转到工具 - >选项 - >调试并选择“启用.NET Framework源步进”。请注意,这可能需要一段时间才能下载所有必需的文件,但这样您就可以进入form.Show()并查看到底发生了什么。

答案 9 :(得分:0)

好的,事实证明它比我想象的更简单,更通用,但仍然很奇怪和模糊。

如果你像我们一样加载/关闭表单时保存/加载表单大小/位置/ WindowState,你必须确保OnLoad方法首先调用base.OnLoad,以便激活Form Load事件处理程序,然后设置属性。如果表单从Load方法内部调用Close,则不这样做只会导致问题。表单结束事件完成后,您将在Show调用中获得ObjectDisposedException。

我的头疼。

答案 10 :(得分:0)

Form.Shown()也是诀窍。

答案 11 :(得分:0)

据我了解,设置表单的DialogResult将关闭表单 - 可能必须不是DialogResult.None。 (即,您不需要再调用Form.Close()方法)。

问题还在于,如果代码中的其他地方,您正在访问其中的表单或控件的属性,这可能会阻止表单关闭。

如果按照建议,你也可以拥有一个属性,例如

private bool _loadedOk = false; 

在您在初始化代码中设置的表单中。在Form_Loaded之后的一个后续事件中,您然后询问它并关闭表单,如果它是假的。

也许有人可以建议最好的事件来做?

答案 12 :(得分:0)

如果您要关闭表单而不闪烁,我发现的最好方法是重写SetVisibleCore方法:

public partial class MyForm : Form
{

...
    protected override void SetVisibleCore(bool value)
    {
        if (value && !IsHandleCreated && !ContinueLoadingForm())
        {
            base.SetVisibleCore(false);
            this.Close();
            return;
        }

        base.SetVisibleCore(value);
    }
}

然后您可以简单地做到:

...
var myForm = new MyForm();
myForm.Show();
...

仅当ContinueLoadingForm()为true时,才会显示该窗体,这也适用于ShowDialog()和Application.Run()。