从加载处理程序关闭表单

发布于 2024-07-16 09:36:44 字数 1337 浏览 13 评论 0原文

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

基本上我正在创建一个 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()
...等等

这就是我所看到的情况:

  1. 调用 Control.Show()
  2. 我的表单启动
  3. OnFormLoad 方法调用
  4. FormLoad 事件处理程序被调用,其中我调用 this.Close()
  5. 调用 OnFormClosing 方法,调用
  6. FormClosing 事件处理程序
  7. 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:

  1. Control.Show() is called
  2. my form is launched
  3. the OnFormLoad method is called
  4. the FormLoad event handler is called, inside of which I call this.Close()
  5. the OnFormClosing method is called
  6. the FormClosing event handler is called
  7. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

感性不性感 2024-07-23 09:36:44

最好的方法:

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

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

The best way to do so :

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

this is the most simple way you wont get ObjectDisposedException

滿滿的愛 2024-07-23 09:36:44

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

您说您先调用 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 then Form.Close() and then the form is Disposed of. Well, unless you use MDI or use ShowDialog that'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.

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 of 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.

落花浅忆 2024-07-23 09:36:44

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

在我的表单上,我重写了 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.

闻呓 2024-07-23 09:36:44

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

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程序可以继续加载。

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.

绳情 2024-07-23 09:36:44

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

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

若水微香 2024-07-23 09:36:44
protected override void CreateHandle()
   {
        base.CreateHandle();

        if (FormMustClose)  //FormMustClose is a variable in the loadevent.
        {
            Close();
        }
    }
protected override void CreateHandle()
   {
        base.CreateHandle();

        if (FormMustClose)  //FormMustClose is a variable in the loadevent.
        {
            Close();
        }
    }
失眠症患者 2024-07-23 09:36:44

在我看来,无需仔细观察,完成您想要的事情的最干净的方法可能是创建一个从 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 override OnFormLoad(...) and/or Show() to check for your condition and cancel out early.

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

很快妥协 2024-07-23 09:36:44

一种可能性:

他们可能在此表单上有一个计时器,该计时器正在其 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...

幻想少年梦 2024-07-23 09:36:44

您是否尝试过单步执行 .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.

笑着哭最痛 2024-07-23 09:36:44

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

如果您像我们一样在表单加载/关闭时保存/加载表单 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.

枕花眠 2024-07-23 09:36:44

Form.Shown() 也是窍门。

Form.Shown() Is the trick too.

对你再特殊 2024-07-23 09:36:44

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

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

如果正如所建议的那样,您

private bool _loadedOk = false; 

在初始化代码中设置了一个属性,例如在您的表单中,这也可能是最好的。 在 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.

private bool _loadedOk = false; 

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??

泪意 2024-07-23 09:36:44

如果您想关闭表单而不闪烁,我发现的最好方法是覆盖 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()以及。

If you want to close the form without flicker, the best way I found was override SetVisibleCore Method:

public partial class MyForm : Form
{

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

        base.SetVisibleCore(value);
    }
}

Then you can simply do:

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

The Form only will appear if ContinueLoadingForm() be true, this works with ShowDialog() and Application.Run() as well.

哥,最终变帅啦 2024-07-23 09:36:44

在此线程中扩展 RCMAN 的答案(这让我完成了 99% 的任务)...

这是我最终使用的代码,它也避免了屏幕闪烁:

Me.FormBorderStyle = FormBorderStyle.None
Me.Opacity = 0.01
Me.MinimumSize = New Size(1, 1)
Me.Size = Me.MinimumSize
Me.Location = New Point(1, 1)
BeginInvoke(New MethodInvoker(AddressOf Me.Close))

此外,为了避免出现消息“此程序可能不会已正确运行”我应用了 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:

Me.FormBorderStyle = FormBorderStyle.None
Me.Opacity = 0.01
Me.MinimumSize = New Size(1, 1)
Me.Size = Me.MinimumSize
Me.Location = New Point(1, 1)
BeginInvoke(New MethodInvoker(AddressOf Me.Close))

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文