10

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

基本上我正在创建一个实例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(Boolean value)
at System.Windows.Forms.Control.Show()
...等。

这就是我所看到的:

  1. Control.Show()叫做
  2. 我的表格已启动
  3. OnFormLoad方法被调用
  4. 事件FormLoad处理程序被调用,在其中我调用this.Close()
  5. OnFormClosing方法被调用
  6. 事件FormClosing处理程序被调用
  7. Dispose在我的表单及其所有用户控件上调用

然后在接近Control.Show()方法末尾的某个地方,它尝试获取表单的句柄,因为对象被标记为已释放,所以它吓坏了并抛出异常。

我真正的问题是,为什么我可以毫无例外地在我拥有的所有其他表格上做同样的事情?是GC问题吗?我试过在GC.Collect()之后立即打电话this.Close(),这没有什么区别。就像我说的,它 100% 的发生在这个表单上,从来没有发生在其他任何地方,不管子用户控件、表单变量的范围等。

有任何想法吗?

4

13 回答 13

34

最好的方法:

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

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

于 2013-07-22T08:49:57.493 回答
8

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

你说你打电话Control.Show(),然后Form.Close()表格就被处理掉了。好吧,除非您使用 MDI 或使用ShowDialog文档中的内容。虽然,Close()文档的简短版本是“关闭表单”,但它实际上也在某些条件下隐式处理它。

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

如果您想再次显示表单。使用Hide()方法而不是Close().

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

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

于 2011-10-29T13:33:58.273 回答
7

好的,讨厌回答我自己的问题,但这让我发疯了,这是我见过的最难重现的错误之一。

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

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

因此,基本上,如果该表单要从其 Load 事件中调用 Close,则您无法在表单的 OnFormClosing 方法中访问表单显示属性。(除非您先检查 Disposed 道具)

我知道非常具体的 Winforms 智慧,但我还是把它写下来。

于 2009-04-08T19:35:08.900 回答
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。

于 2013-10-30T09:25:40.907 回答
3

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

于 2009-04-08T17:56:22.053 回答
3
protected override void CreateHandle()
   {
        base.CreateHandle();

        if (FormMustClose)  //FormMustClose is a variable in the loadevent.
        {
            Close();
        }
    }
于 2020-05-15T16:22:44.990 回答
1

一种可能:

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

我以前见过这样的表格...

于 2009-04-08T17:56:06.483 回答
0

在我看来,如果不仔细观察,完成您想要的最干净的方法可能是创建一个派生自 的自定义表单类Form,并覆盖OnFormLoad(...)和/或Show()检查您的状况并提前取消。

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

于 2009-04-08T17:55:48.013 回答
0

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

于 2009-04-08T18:10:33.500 回答
0

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

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

我头疼。

于 2009-04-08T20:08:19.333 回答
0

Form.Shown() 也是诀窍。

于 2012-02-11T18:57:57.963 回答
0

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

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

如果按照建议,您有一个属性,例如

private bool _loadedOk = false; 

在您在初始化代码中设置的表单中。在 Form_Loaded 之后的后续事件之一中,您随后会询问它并在它为假时关闭表单。

也许有人可以建议最好的活动来做到这一点?

于 2015-11-18T23:44:11.287 回答
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()。

于 2019-11-13T18:47:09.933 回答