WinForm持有窗体但是无法访问已释放的对象

本文探讨了在C# WinForm应用中遇到的未处理ObjectDisposedException异常问题。当尝试访问已关闭的子窗口时,错误提示无法访问已释放的对象。问题源于C#对象的非确定性销毁,导致子窗口对象可能为null但实际已被销毁。解决方案是对菜单点击事件进行改进,检查子窗口是否真的已关闭,从而避免异常。通过确保在子窗口关闭后将对象设置为null,可以防止重复创建和访问已销毁的对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、C#在父窗口中调用子窗口的过程

1、 创建子窗口对象

2、 显示子窗口对象

 

二、笔者的程序中,主窗体MainFrm通过菜单调用子窗口ChildFrm。在窗体中定义了子窗口对象,然后在菜单项点击事件中,加入了如下代码来创建和显示子窗口:

Private childFrm myChildFrm = null; //定义子窗口对象

private void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e)
{        
    myChildFrm = new ChildFrm();//创建子窗口对象        
    myChildFrm.Show();//显示子窗口        
    myChildFrm.Focus();//使子窗口获得焦点
}

三、当点击菜单中的OpenChild项时,创建了子窗口并显示在最前面。此时如果关闭子窗口再点击菜单打开,不会有问题。但是如果子窗口没有关闭的情况下,再次点击菜单中的OpenChild项,则会再创建一个子窗口。两个子窗口具有相同的内容,这不是我们所希望看到的。

为此,对菜单项点击事件做如下改进:

private void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e)
{    
    if(myChildFrm != null)   
        {        
            myChildFrm.Show();//显示子窗口        
            myChildFrm.Focus();//使子窗口获得焦点    
        }    
        else    
        {        
            myChildFrm = new ChildFrm();//创建子窗口对象       
            myChildFrm.Show();//显示子窗口        
            myChildFrm.Focus();//使子窗口获得焦点    
        }
}

这样修改的目的是:当子窗口对象存在时,直接显示子窗口。当子窗口不存在时,创建子窗口,然后再显示。
现在来检验效果:当第一次点击OpenChild菜单项时,创建子窗口并正确显示。不关闭子窗口的情况下再点击OpenChild菜单项,子窗口只显示了一个,说明按预期工作了。现在,我们关闭子窗口,再点击OpenChild菜单项,程序在运行到下面这个语句时出现“未处理ObjectDisposedException”异常。

if(myChildFrm != null)
{    
   myChildFrm.Show();//显示子窗口
}

错误信息:无法访问已释放的对象。对象名:“childFrm”。

这就让人奇怪了。如果子窗口没有被销毁,那它就应该能够正确显示。点击了关闭子窗口,显然应该子窗口已经销毁了,按理myChildFrm等于null,运行的时候应该直接运行else后面的语句块,为什么却进入了满足myChildFrm!=null的语句块呢?

四、其实,这个问题与C#的垃圾回收有关。垃圾回收器管理所有的托管对象,所有需要托管数据的.NET语言(包括C#)都受运行库的垃圾回收器的制约。垃圾回收器可以确定运行垃圾回收的最佳时间,自动进行垃圾回收。然而垃圾回收的一个产物是:C#对象没有确定性毁坏。所以会出现子窗口对象已被销毁,但又不为null,故出现访问时产生“未处理ObjectDisposedException”异常(来自于“从小处看C#.net垃圾回收”一文)。

①如何解决这个题,有人提出:应该应该彻底回收Child所占的资源。并提供了解决方法(请搜索“从小处看C#.net垃圾回收”查看相关情况)。

②其实,现在我们需要解决的问题仅仅是:子窗口已经被销毁,但对象却不为null。只需要对你窗口中的菜单点击事件函数进行简单修改就可以了。

private void OpenChildFrmToolStripMenuItem_Click(object sender, EventArgs e)
{    
    if(myChildFrm == null || myChildFrm.IsDisposed)    
    {         
         myChildFrm = new ChildFrm();    
    }    
    myChild..MdiParent = this; //建立父子关系        
    myChildFrm.Show(); //显示子窗口    
    myChildFrm.Focus();  //子窗口获得焦点
}

这样,就能够如我们如愿般调用子窗口了。

 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值