从加载处理程序关闭表单
我有一种非常奇怪的行为,似乎只发生在一种形式上。
基本上我正在创建一个 Form
实例,并调用 Show()
以非阻塞方式显示表单。 在该表单的 Load 事件处理程序中,我有一些逻辑可能在某些情况下调用 this.Close() 。 这将关闭表单,但客户端代码中的表单 Show()
方法会抛出 ObjectDisposeException
。
ObjectDisposeException 的堆栈跟踪如下:
在 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()
...等等
这就是我所看到的情况:
- 调用
Control.Show()
- 我的表单启动
OnFormLoad
方法调用FormLoad
事件处理程序被调用,其中我调用this.Close()
,- 调用
OnFormClosing
方法,调用 FormClosing
事件处理程序Dispose< /code> 在我的表单及其所有用户控件上被调用
,然后在 Control.Show()
方法末尾的某个地方,它尝试获取表单的句柄,这会导致崩溃并抛出异常异常,因为该对象被标记为已处置。
我真正的问题是,为什么我可以在我拥有的所有其他表单上毫无例外地执行完全相同的操作? 是GC的问题吗? 我尝试在 this.Close()
之后调用 GC.Collect()
,但没有什么区别。 就像我说的,它 100% 发生在这个表单上,而不会发生在其他任何地方,无论子用户控件、表单变量的范围等如何。
有什么想法吗?
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 Load
event 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
.
The stack trace from the ObjectDisposedException is as follows:
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.
This is what I'm seeing happen:
Control.Show()
is called- my form is launched
- the
OnFormLoad
method is called - the
FormLoad
event handler is called, inside of which I callthis.Close()
- the
OnFormClosing
method is called - the
FormClosing
event handler is called Dispose
is called on my form and all it's user controls
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.
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.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
最好的方法:
这是您不会得到 ObjectDisposeException 的最简单方法
The best way to do so :
this is the most simple way you wont get ObjectDisposedException
我知道这是一个老问题,但似乎没有人发布明显的答案。
您说您先调用
Control.Show()
,然后调用Form.Close()
,然后将表单处理掉。 好吧,除非您使用 MDI 或使用ShowDialog
,否则就像记录的那样。 尽管Close()
文档的简短版本是“关闭表单”,但它实际上也在某些条件下隐式地处理它。见备注部分:
http://msdn.microsoft.com/en -us/library/system.windows.forms.form.close.aspx
如果您想再次显示表单。 使用
Hide()
方法而不是Close()
。希望能帮助其他正在寻找的灵魂。
伙计们,不要停止搜索“我不知道为什么它有时会起作用”。 这就变成了有缺陷的软件,有很多防御性的“我会再次调用这个方法,以防万一”的东西。 不好。
I know this is an old issue but no one seemed to have posted the obvoius answer.
You say you call
Control.Show()
and thenForm.Close()
and then the form is Disposed of. Well, unless you use MDI or useShowDialog
that's just as documented. Though, the short version of theClose()
documentation is "Closes the form", it actually also disposes it implicitly under certain conditions.See the remarks section:
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 ofClose()
.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.
好吧,我不想回答我自己的问题,但这让我发疯,这是我见过的最难重现的错误之一。
在我的表单上,我重写了 OnFormLoad 和 OnFormClose 方法,在其中将表单的大小、位置和 WindowState 保存到注册表或从注册表恢复。 我取出这段代码并解决了问题。 奇怪的是,我把它放回去,问题就没有再出现。
我终于重现了这个问题:你必须让窗体完全打开,最大化它,然后关闭它,这样最大化状态才能保存到注册表中。 然后,当您再次打开它时,它会将其设置为最大化,并且如果它在加载处理程序中关闭,它会在关闭时尝试访问大小/位置。 显然,当且仅当表单最大化时,在 OnFormClosing 方法中访问这些值会导致表单尝试聚焦,这是非法的,因为表单已被释放。
所以基本上,如果该表单要从其 Load 事件中调用 Close,则您无法在表单的 OnFormClosing 方法中访问表单显示属性。(除非您先检查 Dispose 属性)
我知道这是非常具体的 Winforms 智慧,但无论如何我还是要把它写下来。
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.
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.
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)
pretty specific piece of Winforms wisdom I know, but I'm writing it down anyway.
如果您想像用户按下右上角的叉号一样关闭表单(通常意味着取消),只需添加以下代码即可。
这也适用于表单加载函数:
如果您不希望表单在短时间内可见,请将 Visible 属性设置为 false(例如在设计器或构造函数中),并在确定时将其设置回 true程序可以继续加载。
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 also works in the form load function:
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.
在加载事件中关闭表单并不是一个好主意。 在激活事件之后执行此操作。
In load event is not realy good idea close the form. Do it after the Activated event.
在我看来,无需仔细观察,完成您想要的事情的最干净的方法可能是创建一个从
Form
派生的自定义表单类,并覆盖OnFormLoad(...)
和/或Show()
检查您的状况并提前取消。也就是说,我不知道为什么它有时会起作用,有时却不起作用。
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 overrideOnFormLoad(...)
and/orShow()
to check for your condition and cancel out early.That said, I don't know why it would work sometimes and not other times.
一种可能性:
他们可能在此表单上有一个计时器,该计时器正在其 FormLoad 事件中初始化并启用。 如果计时器在触发时尝试访问表单,则在关闭表单之前,还需要禁用并停止计时器。
我之前见过这样做的表格......
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.
I've seen forms before that do this...
您是否尝试过单步执行 .net 代码以查看发生异常时正在调用哪一行代码? 如果您有 VS 2008,您可以通过转到“工具 -->”来执行此操作。 选项 --> 调试并选择启用 .NET Framework 源步进。 请注意,这可能需要一段时间才能下载所有必需的文件,但这样您就可以进入 form.Show() 并准确查看发生了什么。
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.
好吧,事实证明它比我想象的更简单、更通用,但仍然很奇怪和晦涩。
如果您像我们一样在表单加载/关闭时保存/加载表单 Size/Location/WindowState,则必须确保 OnLoad 方法首先调用 base.OnLoad 以便触发 Form Load 事件处理程序,然后设置的属性。 如果表单从 Load 方法内部调用 Close,不这样做只会导致问题。 表单关闭事件完成后,您将在 Show 调用中收到 ObjectDisposeException。
我头疼。
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.
My head hurts.
Form.Shown() 也是窍门。
Form.Shown() Is the trick too.
据我了解,设置表单的 DialogResult 将关闭表单 - 可能必须是 DialogResult.None 以外的值。 (即您不需要调用 Form.Close() 方法)。
问题还在于,如果在代码的其他地方,您正在访问表单或其中的控件的属性,则可能会阻止表单关闭。
如果正如所建议的那样,您
在初始化代码中设置了一个属性,例如在您的表单中,这也可能是最好的。 在 Form_Loaded 之后的后续事件之一中,您可以询问此事件,如果为 false,则关闭表单。
也许有人可以建议最好的活动来做到这一点?
As I understand it, setting the DialogResult of the form will close the form - may have to be other than DialogResult.None. (i.e. you don't need to then call the Form.Close() method).
The issue is also in part that if elsewhere in code, you are accessing a property of the form or control within it, that may prevent the form from closing.
It may also be best if as has been suggested, you have a property e.g.
in your form which you set in your initialisation code. In one of the later events after Form_Loaded, you then interrogate this and close the form if it's false.
Perhaps someone can suggest the best event to do this in??
如果您想关闭表单而不闪烁,我发现的最好方法是覆盖 SetVisibleCore 方法:
然后您可以简单地执行以下操作:
仅当ContinueLoadingForm()为true时才会出现表单,这适用于ShowDialog()和Application.Run()以及。
If you want to close the form without flicker, the best way I found was override SetVisibleCore Method:
Then you can simply do:
The Form only will appear if ContinueLoadingForm() be true, this works with ShowDialog() and Application.Run() as well.
在此线程中扩展 RCMAN 的答案(这让我完成了 99% 的任务)...
这是我最终使用的代码,它也避免了屏幕闪烁:
此外,为了避免出现消息“此程序可能不会已正确运行”我应用了 mik 此处所述的清单更改:
如何在 Vista 上防止“此程序可能未正确安装”消息
Expanding on RCMAN's answer in this thread (which got me 99% of the way to the finish line) ...
Here is the code I ended up using which also avoids the screen flicker:
Additionally, to avoid the message "This program might not have run correctly" I applied a manifest change as described by mik here:
How to prevent "This program might not have installed correctly" messages on Vista