简体   繁体   English

从负载处理程序关闭表单

[英]Closing a form from the Load handler

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.基本上我正在创建一个Form的实例,并调用Show()以非阻塞地显示表单。 In that form's Load event handler, I have some logic that may call this.Close() under certain circumstances.在该表单的Load事件处理程序中,我有一些逻辑可以在某些情况下调用this.Close() This closes the form, but then the form Show() method in the client code throws an ObjectDisposedException .这将关闭表单,但随后客户端代码中的表单Show()方法会抛出ObjectDisposedException

The stack trace from the ObjectDisposedException is as follows:来自 ObjectDisposedException 的堆栈跟踪如下:

at System.Windows.Forms.Control.CreateHandle()在 System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.Form.CreateHandle()在 System.Windows.Forms.Form.CreateHandle()
at System.Windows.Forms.Control.get_Handle()在 System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.ContainerControl.FocusActiveControlInternal()在 System.Windows.Forms.ContainerControl.FocusActiveControlInternal()
at System.Windows.Forms.Form.SetVisibleCore(Boolean value)在 System.Windows.Forms.Form.SetVisibleCore(布尔值)
at System.Windows.Forms.Control.Show()在 System.Windows.Forms.Control.Show()
...etc. ...ETC。

This is what I'm seeing happen:这就是我所看到的:

  1. Control.Show() is called Control.Show()被称为
  2. my form is launched我的表格已启动
  3. the OnFormLoad method is called调用OnFormLoad方法
  4. the FormLoad event handler is called, inside of which I call this.Close()调用FormLoad事件处理程序,在其中调用this.Close()
  5. the OnFormClosing method is called调用OnFormClosing方法
  6. the FormClosing event handler is called调用FormClosing事件处理程序
  7. Dispose is called on my form and all it's user controls在我的表单及其所有用户控件上调用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()方法末尾的某个地方,它试图获取表单的句柄,这会吓坏并抛出异常,因为 object 已标记为已处置。

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?这是GC问题吗? I've tried putting a GC.Collect() call right after the this.Close() and it makes no difference.我试过在this.Close()之后调用GC.Collect() ) ,但没有任何区别。 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.就像我说的,它 100% 的时间都发生在这个表单上,而不会在其他任何地方发生,无论子用户控件、表单变量的 scope 等等。

Any ideas?有任何想法吗?

The best way to do so :最好的方法是:

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

this is the most simple way you wont get ObjectDisposedException这是你不会得到 ObjectDisposedException 的最简单的方法

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.你说你调用Control.Show()然后Form.Close()然后表单被处理。 Well, unless you use MDI or use ShowDialog that's just as documented.好吧,除非您使用 MDI 或使用ShowDialog ,否则就像文档一样。 Though, the short version of the Close() documentation is "Closes the form", it actually also disposes it implicitly under certain conditions.虽然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.不好。

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.在我的表单上,我覆盖了 OnFormLoad 和 OnFormClose 方法,在那里我将表单的大小、位置和 WindowState 保存/恢复到注册表中/从注册表中恢复。 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.显然,在 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 智慧,但我还是把它写下来了。

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。

In load event is not realy good idea close the form.在加载事件中关闭表单并不是一个好主意。 Do it after the Activated event.在 Activated 事件之后进行。

protected override void CreateHandle()
   {
        base.CreateHandle();

        if (FormMustClose)  //FormMustClose is a variable in the loadevent.
        {
            Close();
        }
    }

One possibility:一种可能:

They may have a timer on this form, that is being initialized and enabled in their FormLoad event.他们可能在这个表单上有一个计时器,它在他们的 FormLoad 事件中被初始化和启用。 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...我之前看过表格做这个......

As I understand it, setting the DialogResult of the form will close the form - may have to be other than DialogResult.None.据我了解,设置窗体的 DialogResult 将关闭窗体 - 可能必须不是 DialogResult.None。 (ie you don't need to then call the Form.Close() method). (即您不需要然后调用 Form.Close() 方法)。

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 eg如果按照建议,您有一个财产,例如

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.在 Form_Loaded 之后的后续事件之一中,您会询问它并在它为 false 时关闭该表单。

Perhaps someone can suggest the best event to do this in??也许有人可以建议最好的活动来做到这一点?

If you want to close the form without flicker, the best way I found was override SetVisibleCore Method:如果你想在不闪烁的情况下关闭表单,我发现的最好方法是覆盖 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);
    }
}

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.只有当 ContinueLoadingForm() 为 true 时才会出现表单,这也适用于 ShowDialog() 和 Application.Run()。

Expanding on RCMAN's answer in this thread (which got me 99% of the way to the finish line)...在此线程中扩展 RCMAN 的答案(这使我达到了终点线的 99%)...

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:此外,为了避免出现“此程序可能未正确运行”消息,我应用了 mik 此处所述的清单更改:

How to prevent "This program might not have installed correctly" messages on Vista 如何防止在 Vista 上出现“此程序可能未正确安装”消息

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.也就是说,我不知道为什么它有时会起作用,而有时却不起作用。

Have you tried stepping into the .net code to see what line of code is being called when the exception is occuring?您是否尝试过进入 .net 代码以查看发生异常时调用的代码行? If you have VS 2008 you can do so by going to Tools --> Options --> Debugging and select the Enable .NET Framework Source Stepping.如果您有 VS 2008,您可以通过转至工具 --> 选项 --> 调试并选择启用 .NET Framework 源步进来执行此操作。 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.请注意,这可能需要一段时间来下载所有必要的文件,但这样您就可以进入 form.Show() 并确切地看到发生了什么。

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.如果您像我们一样在表单加载/关闭时保存/加载表单 Size/Location/WindowState,您必须确保 OnLoad 方法首先调用 base.OnLoad 以便触发 Form Load 事件处理程序,然后设置属性。 Not doing so will only cause a problem if the form calls Close from inside the Load method.如果窗体从 Load 方法内部调用 Close,不这样做只会导致问题。 You'll get an ObjectDisposedException on the Show call after the form closing event is done.窗体关闭事件完成后,您将在 Show 调用中收到 ObjectDisposedException。

My head hurts.我头疼。

Form.Shown() 也是诀窍。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM