一、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(); //子窗口獲得焦點
}
這樣,就能夠如我們如願般調用子窗口了。