代码之家  ›  专栏  ›  技术社区  ›  LoveMeSomeCode

从加载处理程序关闭窗体

  •  8
  • LoveMeSomeCode  · 技术社区  · 15 年前

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

    基本上我正在创建 Form 呼唤 Show() 以非阻塞方式显示窗体。以那种形式 Load 事件处理程序,我有一些逻辑可以调用 this.Close() 在某些情况下。这将关闭窗体,但窗体 显示() 客户端代码中的方法引发 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()上
    ……等

    这就是我所看到的:

    1. Control.Show() 被称为
    2. 我的表单已启动
    3. 这个 OnFormLoad 方法被调用
    4. 这个 FormLoad 调用事件处理程序,在其中调用 这个()
    5. 这个 OnFormClosing 方法被调用
    6. 这个 FormClosing 调用事件处理程序
    7. Dispose 在我的窗体及其所有用户控件上调用

    然后就在 控件() 方法,它尝试获取窗体的句柄,该句柄会异常并引发异常,因为对象被标记为已释放。

    我真正的问题是,为什么我可以毫无例外地在其他任何形式上做同样的事情?是GC问题吗?我试过把 GC.Collect() 呼叫后立即 这个() 这没什么区别。如我所说,它在这个表单上发生的时间是100%,而在其他任何地方都不会发生,不管子用户控件、表单变量的范围等如何。

    有什么想法吗?

    11 回复  |  直到 8 年前
        1
  •  8
  •   Ajay    10 年前

    我知道这是一个古老的问题,但似乎没有人发表过这样的回答。

    你说你打电话来 Control.Show() 然后 Form.Close() 然后表格被处理掉。好吧,除非你使用MDI或者 ShowDialog 这是有记录的。尽管如此, Close() 文档是“关闭表单”,实际上它也在某些条件下隐式地处理它。

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

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

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

    伙计们,不要停止搜索“我不知道为什么它有时会起作用”。这就变成了一个有很多防御性的bug软件,“我会再次调用这个方法,以防万一”之类的东西。不好的。

        2
  •  25
  •   RcMan    11 年前

    最好的方法是:

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

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

        3
  •  5
  •   LoveMeSomeCode    15 年前

    好吧,我不想回答我自己的问题,但这让我发疯,这是我见过的最难复制的虫子之一。

    在我的窗体上,我将覆盖onformLoad和onformClose方法,在这里我将窗体的大小、位置和窗口状态保存到/从注册表中恢复。我取出了这个代码,它解决了这个问题。奇怪的是,我把它放回原处,问题却没有回来。

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

    因此,基本上,如果表单将从其加载事件调用Close,则无法访问表单的onformClosing方法中的表单显示属性。(除非先检查释放的属性)

    我知道Winforms智慧中相当具体的一部分,但我还是写下来了。

        4
  •  4
  •   Harald Coppoolse    9 年前

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

    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。

        5
  •  3
  •   TcKs    15 年前

    在加载事件中,关闭窗体不是一个好主意。在激活事件之后执行。

        6
  •  1
  •   Reed Copsey    15 年前

    一种可能性:

    它们可能在此窗体上有一个计时器,该计时器正在其FormLoad事件中初始化和启用。如果计时器试图在窗体被激发时访问它,则在窗体关闭之前,也需要禁用和停止计时器。

    我以前见过这样的表格…

        7
  •  0
  •   mqp    15 年前

    在我看来,在没有仔细观察的情况下,实现所需功能的最干净的方法可能是创建一个从 Form 和重写 OnFormLoad(...) 和/或 Show() 检查你的情况,提前取消。

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

        8
  •  0
  •   Ross Goddard    15 年前

    是否尝试单步执行.NET代码以查看发生异常时正在调用哪一行代码?如果您有vs 2008,可以通过转到“工具”-->选项”->调试并选择“启用.NET框架源代码步进”来实现。请注意,下载所有必需的文件可能需要一段时间,但这样您就可以进入form.show()并查看到底发生了什么。

        9
  •  0
  •   LoveMeSomeCode    15 年前

    好吧,结果发现它比我想象的要简单一些,更通用一些,但是仍然很奇怪,很模糊。

    如果您像我们一样在窗体加载/关闭时保存/加载窗体大小/位置/窗口状态,则必须确保onload方法首先调用base.onload,以便触发窗体加载事件处理程序,然后设置属性。不这样做只会在窗体从Load方法内部调用Close时导致问题。完成窗体关闭事件后,您将在Show调用上获得一个ObjectDisposedException。

    我头痛。

        10
  •  0
  •   Vahid Ghadiri    13 年前

    form.shown()也是诀窍。

        11
  •  0
  •   mattpm    9 年前

    据我所知,设置窗体的DialogResult将关闭窗体-可能必须不是DialogResult.None。(即,您不需要调用form.close()方法)。

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

    如有人建议,最好是你有一处房产,例如

    private bool _loadedOk = false; 
    

    以您在初始化代码中设置的形式。在加载表单后的一个后续事件中,您将询问此问题并关闭该表单(如果它是假的)。

    也许有人能推荐最好的活动来做这件事??