今天關閉一個表單,報出這樣的一個錯誤"在建立視窗控制代碼之前,不能在控制項上調用 Invoke 或 BeginInvoke。",這個不用多想,肯定是那個地方沒有釋放掉。
既然碰到這個問題,先不說問題本身,來說說其他的一些事情。
winform最常見的是datagridview這個控制項,不管重寫還是怎麼,很多資料的操作都是用datagridview來展示的,因此,它的非同步呼叫也算是比較多的一類了。
比如:
1 從資料庫中讀取大量資料(所謂的分頁讀取不在這個範疇)
2 操作datagridview,然後一段時間後改變或者填充dtagridview
3 datagridview本身的一些效果,比如旋轉的延時等待,或者其他
不用非同步肯定會出現死機的情況,用了非同步可能也要注意一些情況
一個簡單的例子:
一個showdialog表單裡有個一個datagridview,我用非同步讀取資料,但是沒讀完,我關了表單,這時候,資料讀完了,要執行
datagridview.source=??
這個時候,會出錯,可能不是"在建立視窗控制代碼之前,不能在控制項上調用 Invoke 或 BeginInvoke。"這個錯誤,而是"資源已經釋放之類的",那咱們看看下面的幾個方法。
1 this.components 這個屬性
/// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null;
每一個Designer.cs裡都有一個這個東西,IContainer介面相當於是一個容器,一個頁面全部的東西都會放在裡面,你拖一個button或者label都會放在裡面,筆者覺得,這個其實就是wpf的一個容器的概念,你可以從root尋找到每一個控制項,而IContainer也可以找到你想要的控制項,Active啟用或者不啟用會用到這個。
表單釋放,components 也會釋放
/// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
這個方法會釋放,所以可以當做判斷表單是否釋放的一個依據,但是筆者不推舉
2 this.IsDisposed
這個是判斷是否已經釋放了,用這個判斷比 components 要好一些,具體的原因是components在表單關閉後可能沒有釋放,而this.IsDisposed表單必然已經釋放了,當表單是MID模式的時候,由於線程或其他原因,表單的關閉可能不會釋放
3 IsHandleCreated
控制代碼是否建立
當子空間控制代碼建立了,而它的parent的控制代碼由於其他原因沒有建立或者已經釋放了,則也會出現其他問題,所以這個可以通過Parent.IsHandleCereated來盤點父控制代碼是否存在或者已經建立
上面幾個方法可以組合用,筆者判斷的時候差不多用後兩個
現在說說這兩個Invoke 和 BeginInvoke
Control.Invoke:在擁有此控制項的線程上先進先出順序執行委託
Control.BeginInvoke:在擁有此控制項線程上非同步執行委託,也就是可能並非順序執行,這個有點熟悉,貌似說過了
最後說說解決方案:
在Invoke(....)之前加上1 this.components==null 2 this.IsDisposed 3 IsHandleCreated 來return 不執行invoke就可以,當然只是我針對自己遇到的解決的,可能並不適合其他的,但是總不會脫離其中
set { if (IsDisposed ||!this.Parent.IsHandleCreated) return; this.Invoke((System.Action)delegate() { }); }
這是我的判斷