我有一种非常奇怪的行为,似乎只发生在一个表格上。
基本上我正在创建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(布尔值)
在System.Windows.Forms.Control.Show()
...等
这就是我所看到的:
Control.Show()
被称为OnFormLoad
方法FormLoad
事件处理程序,我在其中调用this.Close()
OnFormClosing
方法FormClosing
事件处理程序Dispose
在我的表单上调用,并且所有用户控件都被调用然后在Control.Show()
方法结束的某个地方,它会尝试获取表单的句柄,该表单会因为对象被标记为处置而变形并抛出异常。
我真正的问题是,为什么我可以在我没有例外的其他形式上完成同样的事情?这是GC问题吗?我已经尝试在GC.Collect()
之后立即拨打this.Close()
电话,这没有任何区别。就像我说的那样,无论子用户控件,表单变量的范围等等,它都会在此表单上100%的时间内发生,而且绝不会在其他任何地方发生。
有什么想法吗?
答案 0 :(得分:25)
最好的方法:
this.BeginInvoke(new MethodInvoker(this.Close));
这是最简单的方法,你不会得到ObjectDisposedException
答案 1 :(得分:8)
我知道这是一个老问题,但似乎没有人发布了obvoius答案。
您说您拨打Control.Show()
,然后拨打Form.Close()
,然后表单就是Disposed of。好吧,除非您使用MDI或使用与文档一样的ShowDialog
。虽然,Close()
文档的简短版本是“关闭表单”,但它实际上也在某些条件下隐式处理它。
请参阅备注部分: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.close.aspx
如果您想再次显示表单。使用Hide()
方法代替Close()
。
希望能帮助其他寻找灵魂。
伙计们,不要停止搜索“我不知道为什么它有时会起作用”。这就变成了有很多防御性的错误软件“我会再次调用这种方法以防万一”。不好。
答案 2 :(得分:6)
好的,不想回答我自己的问题,但这让我疯了,这是我见过的最难复制的错误之一。
在我的表单上,我重写了OnFormLoad和OnFormClose方法,我将表单的大小,位置和WindowState保存/恢复到注册表中。我把这个代码拿出来解决了问题。奇怪的是,我把它放回去,问题没有回来。
我终于重现了这个问题:你必须让表单完全打开,最大化它,然后关闭它,以便将Maximized状态保存到注册表中。然后当你再次打开它时,它会将它设置为Maximized,如果它在Load处理程序中关闭,它会在它关闭时尝试访问Size / Location。显然,在OnFormClosing方法中访问这些值会导致表单尝试聚焦IF并且仅在表单最大化时才会聚焦,这是非法的,因为表单已被处理。
所以基本上,你不能在窗体的OnFormClosing方法中访问Form显示属性,如果那个窗体要从它的Load事件调用Close。(除非你先检查Disposed prop)
我知道的Winforms智慧非常具体,但无论如何我都是写下来的。
答案 3 :(得分: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。
答案 4 :(得分:3)
在load事件中关闭表单并不是一个好主意。在Activated事件之后执行此操作。
答案 5 :(得分:2)
set_object_terms
答案 6 :(得分:1)
一种可能性:
他们可能在此表单上有一个计时器,正在FormLoad事件中初始化并启用它。如果计时器试图在表单被触发时尝试访问该表单,则需要在表单关闭之前禁用和停止计时器。
在此之前我见过表格......
答案 7 :(得分:0)
在我看来,如果不仔细研究它,最简洁的方法就是创建一个自定义的表单类来完成你想要的,可以创建Form
,并覆盖OnFormLoad(...)
和/或{ {1}}检查您的情况并尽早取消。
那就是说,我不知道为什么它有时会起作用,而不是其他时候。
答案 8 :(得分:0)
您是否曾尝试单步执行.net代码以查看发生异常时调用的代码行?如果您有VS 2008,可以转到工具 - >选项 - >调试并选择“启用.NET Framework源步进”。请注意,这可能需要一段时间才能下载所有必需的文件,但这样您就可以进入form.Show()并查看到底发生了什么。
答案 9 :(得分:0)
好的,事实证明它比我想象的更简单,更通用,但仍然很奇怪和模糊。
如果你像我们一样加载/关闭表单时保存/加载表单大小/位置/ WindowState,你必须确保OnLoad方法首先调用base.OnLoad,以便激活Form Load事件处理程序,然后设置属性。如果表单从Load方法内部调用Close,则不这样做只会导致问题。表单结束事件完成后,您将在Show调用中获得ObjectDisposedException。
我的头疼。
答案 10 :(得分:0)
Form.Shown()也是诀窍。
答案 11 :(得分:0)
据我了解,设置表单的DialogResult将关闭表单 - 可能必须不是DialogResult.None。 (即,您不需要再调用Form.Close()方法)。
问题还在于,如果代码中的其他地方,您正在访问其中的表单或控件的属性,这可能会阻止表单关闭。
如果按照建议,你也可以拥有一个属性,例如
private bool _loadedOk = false;
在您在初始化代码中设置的表单中。在Form_Loaded之后的一个后续事件中,您然后询问它并关闭表单,如果它是假的。
也许有人可以建议最好的事件来做?
答案 12 :(得分: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()。