Windows Form Designer 續篇 -- components

上回咱們談到了components,這回咱們就來聊聊這components倒底是怎麼一回事。

    上回咱們談到了components,這回咱們就來聊聊這components倒底是怎麼一回事。

 

從Dispose談起

       在建立自訂類別時通常都會建議要實做 IDisposable 介面,使得該類別能具備Dispose方法 (當然你也可以不需要Implements IDisposable也能寫出這方法來,只要記得起來如何正確實做哪些成員即可,老實講我是完全記不起來的啦),Dispose方法的實做提供類別能夠在使用完畢後標註自己的狀態為可回收,使得CLR能夠在適當時機將這些動態記憶體釋放出來。

        關於Dispose,在點部落上有一些朋友嘔心瀝血的作品可以參考:

        Top Cat:物件Object的New,Dispose與Connection的Open,Close概念分享

        Rico:[C#][Tips]Dispose是否影響Connection Pooling? 

                  [C#][Tips]using陳述式

        Jeff Yeh:DB Connection 的Close與Dispose

        在MSDN文件庫中有個章節 [記憶體回收的基本概念] 說明了CLR的記憶體回收基本觀念。

 

IContainer?

        在前文提到了在Designer程式碼中的變數宣告 Private components As System.ComponentModel.IContainer ,這個變數的型別是IContainer;MSDN文件庫描述它是這樣的東西:為容器 (Container) 提供功能。 容器是邏輯上包含零或多個元件的物件。不過其實備註寫的比較精彩 (所以說要看備註啊):

      容器是封裝和追蹤零或多個元件的物件。 在本文中,內含項目是指邏輯的而非視覺上的內含項目。 元件和容器可以用於各種案例中,包括視覺的和隱藏式的案例在內。

實作者注意事項

      若要成為容器,類別必須實作 IContainer 介面,這個介面支援加入、移除和擷取元件的方法

 

      講了個半天就是說『所有要成為容器的類別一定要實做這個介面』(同IDisposible介面的說法,如果你真記得起來,沒人非逼你加上一行Implements IContainer不可),所以要成為一個容器至少要具備幾項要求:

  1. 可以加個物件進來容器
  2. 可以把一個物件從容器中移除
  3. 要可以釋放資源,意即要實做Dispose方法
  4. 要可以取得容器內物件的集合

 

       不知道會不會有人想辯稱說他可以Implements IContainer後,每個方法的內容都是沒有程式碼的,那這樣算不算容器?基本上算,只不過那就是個廢物。

 

謎底揭曉

       瞎扯了這麼多還沒講到正題,應該有人開始罵髒話了,請原諒我常常為了補版面一直瞎掰的習慣。

 

       當我們從設計畫面的工具箱拖拉元件到畫面時,有某些玩意兒不是繼承Control類別而來的,例如說 ImageList、Forms.Timer等直接繼承Component 類別並且在建構函式中有一個多載是可以傳入IContainer型別參數的。例如Forms.Timer的建構函式:

5

      來看一下這個建構函式的備註,真是夠了又是備註:

備註

 

       Timer 建構函式可讓您將 Timer 與任何 Container 物件產生關聯。藉由這個方法與 Timer 產生關聯之後,您就會將 Timer 之存留期的控制權交給 Container。如果您在應用程式中使用一些元件,並且想要同步處置所有元件,那麼這個方法會相當實用。例如,如果您讓 ToolTipImageListTimerContainer 產生關聯,那麼在 Container 上呼叫 Dispose 同樣會強制處置這些元件。

      當建立新的計時器時,它是停用的;也就是說,Enabled 設為 false。若要啟用計時器,請呼叫 Start 方法或是將 Enabled 設為 true。

這個執行個體將會一直存在,直到它的容器釋放到記憶體回收。

 

       我把重點用斜體並改為藍色標明,這也就是說,當我們加入了一堆不會列入Form.Controls屬性中的物件時,如果這些個玩意可以和一個Container物件關聯,當我要Dispose它們的時候,只要Dispose該關聯容器就一次解決了,接著下來我們先新建一個專案,然後從工具列拖拉以下三個元件:Timer、ImageList以及Serialport;在Designer程式碼的底端宣告了三個私有變數,如同加入一般的UI控制項一樣:

Friend WithEvents SerialPort1 As System.IO.Ports.SerialPort
Friend WithEvents Timer1 As System.Windows.Forms.Timer
Friend WithEvents ImageList1 As System.Windows.Forms.ImageList

 

         有趣的事情發生在 Private Sub InitializeComponent() 方法中:

 
         1. 它使用Container類別建構函式為components產生了一個執行個體  

            Me.components = New System.ComponentModel.Container()
         2. 它建構這三個元件都使用傳遞IContainer型別參數的建構函式,並以components物件為引數,使這三個元件與components物件產生關聯 

            Me.SerialPort1 = New System.IO.Ports.SerialPort(Me.components)
            Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
            Me.ImageList1 = New System.Windows.Forms.ImageList(Me.components)
         3. 也因為這樣使得 Protected Overrides Sub Dispose(ByVal disposing As Boolean) 方法中的   

            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If

            成為一段有意義的程式碼

       我個人一直覺得寫程式是個重基礎的事情,即使現代的程式開發環境已經進步到如此視覺化的程度,許多的基本道理還是不可以忽視的。拖拖拉拉雖然方便,但追根究底更是樂趣無窮,當我們對於基本道理的瞭解更深一層,在撰寫程式的技巧上也會更進一步,這也就是為什麼點部落這次辦Windows Form修煉活動我會以Windows Form Designer為第一個主題的原因,希望初學者不僅能瞭解Designer中的程式設計原理,更能夠藉由這樣基礎原理的文章能更瞭解基本技巧的重要性,進而加強對於基本原理的學習與理解,而不再一直誤入網路程式文抄公那種錯誤的方向。