C# 从 Load 处理程序关闭表单

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/731068/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:58:06  来源:igfitidea点击:

Closing a form from the Load handler

c#winformsobjectdisposedexception

提问by LoveMeSomeCode

I have a very strange behavior that only seems to happen on one form.

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

Basically I am creating an instance of a Form, and calling Show()to display the form non-blocking. In that form's Loadevent handler, I have some logic that may call this.Close()under certain circumstances. This closes the form, but then the form Show()method in the client code throws an ObjectDisposedException.

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

The stack trace from the ObjectDisposedException is as follows:

ObjectDisposedException 的堆栈跟踪如下:

at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.Form.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.ContainerControl.FocusActiveControlInternal()
at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
at System.Windows.Forms.Control.Show()
...etc.

在 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()
...等。

This is what I'm seeing happen:

这就是我所看到的:

  1. Control.Show()is called
  2. my form is launched
  3. the OnFormLoadmethod is called
  4. the FormLoadevent handler is called, inside of which I call this.Close()
  5. the OnFormClosingmethod is called
  6. the FormClosingevent handler is called
  7. Disposeis called on my form and all it's user controls
  1. Control.Show()叫做
  2. 我的表单已启动
  3. OnFormLoad方法被称为
  4. FormLoad事件处理函数被调用,里面这是我的电话this.Close()
  5. OnFormClosing方法被称为
  6. FormClosing事件处理程序被调用
  7. Dispose在我的表单及其所有用户控件上调用

and then somewhere toward the end of the Control.Show()method, it tries to get a handle to the form, which freaks out and throws an exception because the object is marked disposed.

然后在Control.Show()方法结束时的某个地方,它尝试获取表单的句柄,这会因为对象被标记为已释放而吓坏了并抛出异常。

My real question is, why can I do this exact same thing on every other form I have without exceptions? Is it a GC issue? I've tried putting a GC.Collect()call right after the this.Close()and it makes no difference. Like I said, it happens 100% of the time on this form, and never anywhere else, regardless of child user controls, scope of the form variable, etc.

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

Any ideas?

有任何想法吗?

采纳答案by JonasW

I know this is an old issue but no one seemed to have posted the obvoius answer.

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

You say you call Control.Show()and then Form.Close()and then the form is Disposed of. Well, unless you use MDI or use ShowDialogthat's just as documented. Though, the short version of the Close()documentation is "Closes the form", it actually also disposes it implicitly under certain conditions.

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

See the remarks section: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.close.aspx

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

If you want to show a form again. Use the Hide()method instead of Close().

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

Hope that helps other searching souls.

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

And guys, don't stop searching at "I don't know why it works sometimes". That becomes buggy software with lots of defensive "I'll call this method again just in case" stuff. Not good.

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

回答by mqp

It seems to me, without looking closely at it, that the cleanest way to accomplish what you want might be to make a custom form class deriving from Form, and override OnFormLoad(...)and/or Show()to check for your condition and cancel out early.

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

That said, I don't know why it would work sometimes and not other times.

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

回答by Reed Copsey

One possibility:

一种可能:

They may have a timer on this form, that is being initialized and enabled in their FormLoad event. The timer would need to be disabled and stopped as well, before the form was closed, if the timer is trying to access the form when it's fired.

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

I've seen forms before that do this...

我之前看过表格做这个......

回答by TcKs

In load event is not realy good idea close the form. Do it after the Activated event.

在加载事件中关闭表单并不是一个好主意。在 Activated 事件之后进行。

回答by Ross Goddard

Have you tried stepping into the .net code to see what line of code is being called when the exception is occuring? If you have VS 2008 you can do so by going to Tools --> Options --> Debugging and select the Enable .NET Framework Source Stepping. Be warned, this may take a while to download all of the necessary files, but this way you can step into the form.Show() and see exactly what is going on.

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

回答by LoveMeSomeCode

Ok, hate to answer my own question, but this was driving me nuts, and it was one of the hardest bugs to reproduce I've ever seen.

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

On my form I'm overriding the OnFormLoad and OnFormClose methods, where I save/restore the form's Size, Location, and WindowState to/from the registry. I took this code out and it fixed the problem. The weird thing is, I put it back and the problem didn't come back.

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

I finally reproduced the problem: you have to let the form open fully, maximize it, and then close it so that the Maximized state is saved to the registry. Then when you open it again, it will set it to Maximized, and if it closes in the Load handler, it tries to access the Size/Location as it's closing. Apparently accessing these values in the OnFormClosing method causes the form to try to focus IF AND ONLY IF the form is maximized, which is illegal, since the form has been disposed.

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

So basically, you can't access Form display properties in the OnFormClosing method of a form, if that form is going to call Close from it's Load event.(Unless you check the Disposed prop first)

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

pretty specific piece of Winforms wisdom I know, but I'm writing it down anyway.

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

回答by LoveMeSomeCode

Ok, it turns out it's a little simpler and more generic than I thought, but still weird and obscure.

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

If you're saving/loading the form Size/Location/WindowState when the form loads/closes like we do, you have to make sure that the OnLoad method calls base.OnLoad first so that the Form Load event handler fires, and THEN set the properties. Not doing so will only cause a problem if the form calls Close from inside the Load method. You'll get an ObjectDisposedException on the Show call after the form closing event is done.

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

My head hurts.

我头疼。

回答by Vahid Ghadiri

Form.Shown() Is the trick too.

Form.Shown() 也是诀窍。

回答by RcMan

The best way to do so :

最好的方法是:

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

this is the most simple way you wont get ObjectDisposedException

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

回答by Harald Coppoolse

If you want to close a form as if the user pressed the cross in the upper right corner (usually means cancel), just add the following code.

如果你想关闭一个表单就好像用户按下了右上角的叉号(通常意味着取消),只需添加以下代码。

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

This also works in the form load function:

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

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
}

If you don't want the form to be visible for a short while, set the Visible property false (for example in the designer or constructor), and set it back to true when you are certain the program can continue loading.

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