2014年9月11日 星期四

開始用巨集來操縱EXCEL的物件

現代的程式撰寫主要是以物件導向, 事件驅動的觀念來作. 物件導向是指用程式來摸擬我們的物質世界, 例如一個人. 物件有屬性, 例如人的身高, 體重; 物件有功能或稱為方法, 例如人會工作. 我們可以用程式模擬建造一個人的物件, 它有身高與體重的屬性及工作的方法. 事件驅動是指程式的執行是由發生了一件事才開始, 例如按了滑鼠一下. EXCEL也是由這個觀念建造的, 所以我們也必需學習EXCEL中的物件及事件.
 EXCEL的工作畫面主要是填滿儲存格的工作表, 我們在儲存格內可以任意填入各種鍵盤打的出來的文字組合. 接下來我們要用程式在儲存格內填入文字來摸擬輸入, 透過這個例子來熟悉常用的物件的關係.

請建立一個巨集, 執行以下的程式:

Cells(1,1)=”Hi, 我的EXCEL物件程式

如果一切順利, 調整在sheet1A1儲存格欄寬, 會出現如下圖變化



Cells(1,1)=”Hi, 我的EXCEL物件程式, Cells代表所有的儲存格, 如果要指定個別的儲存格,  要在Cells的後面要加上(列序, 欄序)來指定, 本範例是(1,1), 代表A1儲存格. 程式碼中的=”Hi, 我的EXCEL物件程式”, 是告訴EXCEL Cells(1,1)中填入””內的文字, 如果要填入數字, 就不必使用””號了. = 號在程式中有兩個意思, 在這邊是指定的意思, ”Hi, 我的EXCEL物件程式指定給Cells(1,1).
        如果不順利, 請把下面的程式碼複製到sheet1的程式碼區段中, 然後再執行一次.

Sub MyObjSub()
    Cells(1, 1) = "Hi, 我的EXCEL物件程式"
End Sub

如果還不順利, 請重讀開始第一個VBA程式”.


現在請把工作表畫面轉換到sheet2, 再重新執行一次上面的巨集程式. Sheet2A1儲存格並不會出現"Hi, 我的EXCEL物件程式"這幾個字, 這是因為程式碼是在sheet1物件的程式區段中寫的

sheet2的程式區段打開, 把上述的程式碼複製到sheet2物件的程式區段, 如下圖


VBE中直接執行sheet2的程式碼, 回到EXCEL, sheet2就可以得到相同的結果了

  按巨集工具打開巨集選用視窗


可以看到二個巨集, 名稱都是MyObjSub, 分別放在sheet1 sheet2. EXCEL會依指定的巨集在相關的sheet中的A1儲存格填上文字. 由此類推, 如果在sheet3中也複製程式碼, 這個巨集也會出現在上述視窗中, 執行後讓sheet3A1儲存格出現文字.

當然你會開始抱怨, 這麼麻煩, 相同的事情要一直重複的做, 跟不寫程式一樣費事. 完全正確, 但這個範例只是要說明如何操作EXCEL的主角: 儲存格(Cells), 而你的抱怨是有解的

把程式碼複製到Thisworkbook的程式碼視窗中, 其他sheet中的程式碼都刪除, 如下圖. 注意, 是刪除程式碼, 不是按右上方的[X]把視窗關閉.


回到EXCEL, 在各個sheet中把A1清乾淨, 然後讓EXCEL執行 Thisworkbook.MyObjSub巨集,


你會發現, 不管在那個sheet中執行這個巨集, A1儲存格會出現文字了

  為什麼呢? 我們先來說明EXCEL的物件結構. EXCEL主要的物件結構如下圖


最上層的物件ApplicationEXCEL環境本身. 打開EXCEL的一個檔案後就是活頁簿Workbook, 可以同時開很多檔案, 所以加上(s); Workbook是包含在EXCEL環境中, Application的子物件.一個活頁簿內有多張工作表Sheets, 所以SheetWorkbook的子物件. Sheet是由許多儲存格Cells組成, 但也可以看成是由許多列Rows或是許多欄Columns組成, 所以用了行列Range這個名稱來代表, Sheet的子物件. Cells, RowsColumns都是Range類的物件.

  我們可以用大樓來比喻說明這個觀念, Application想像成一個城市, Workbooks是城市中的一個區域, sheets是區域中的大樓, 大樓中有樓層(Range, row, column)或辦公室(Cells). 要指出某一間辦公室就用城市.區域.大樓.辦公室城市.區域.大樓.樓層.辦公室等階層的方式來定位, 每一層間用”.”號來連結區隔. EXCEL中也是同樣的方法來指出一儲存格: Application.Workbook.sheet.cell, 但因為ApplicationEXCEL本身, 一般都省略不必特別標示.

        回到我們的問題點, 為什麼上述的程式寫在ThisWorkbook區段就可以適用在所有的Sheet, 而寫在Sheet區段中就只適用在自己呢? 這是因為ThisWorkbook包含多個sheet, 在沒有指明的情況下, EXCEL會自動以作用中的sheet為對象來執行程式. EXCEL這個自動的方式有好處也有壞處, 好處是節省寫程式的時間跟彈性大, 壞處是容易出錯, 怕會覆蓋了其他的資料而不易查覺. 以實際的經驗來說, 建議要明確的指出變更要作用在那個工作表中, 而不要利用EXCEL的這個自動功能.

所以, 如果只跟某個sheet有關的程式碼, 可以寫在該sheet的程式視窗中, 如果跟整個Workbook有關的程式碼, 就可以寫在ThisWorkbook的程式視窗中

另外還有一個地方也可以寫程式碼 : 模組. VBA功能列, 按插入à模組


在專案總管的視窗中會出現模組物件, 並打開程式視窗.


一般模組會寫一些各個sheet程序會呼叫的副程式或函數. 另外, 用錄製巨集所得到的程式碼也會出現在這裡, 原因無他, 預設是這些程式碼是要各個sheet都適用的. 

  了解了Excel的物件結構, 想要在那裡填入什麼資料就很容易了. 例如在任何程式區段中執行

sheet1.cells(1,1)="Hi, 你好嗎?"
sheet2.cells(2,1)=4

明確指定了工作表, 不管巨集在寫在那, 都不會產生問題了. 

  但要明確的指定工作表, 首先要了解Excel的工作表名稱規則. 在VBE的左邊上方的專案視窗中收集了這個活頁簿中所有的物件, 如下圖


可以看到有一個活頁簿及3個工作表. 活頁簿的名稱為ThisWorkbook, 第一個工作表的名稱為sheet1(sheet1), 以下第二, 第三類似, 是Excel預設的. 你會覺得奇怪, 為什麼要多"(sheet1)"? 其實刮號外的sheet1是工作表的真實名稱, 是供巨集程式辨識用的, 而刮號裡的sheet1是我們在工作表頁籤中重新命名時所改變的, 可以想作是這工作表的別名. 這兩個名稱都可以修改, 只是刮號外的真實名稱只能在VBE中修改, 修改的位置在專案視窗下方的屬性視窗. 在專案視窗中選取sheet1, 會看到如下圖


你可以看到有一個(Name) 及 Name, 就是改名稱的地方, 只是跟專案視窗中的剛好相反, (Name)是修改真實名稱, 而Name是修改頁籤名稱(別名)的地方. 把(Name) 右邊的"sheet1"改成 "Test" , 而Name 右邊的"sheet1"改成"測試頁" 就可以了解了. 因為專案視窗會依名稱排序, 所以改過的Test會出現到第三個位置, 如下圖.



  這兩種名稱都可以用來指定工作表, 但寫法不同. 例如要show出Test工作表的名稱, 可以這樣寫

直接指名法 : Msgbox Test.Name 或
集合指名法1 : Msgbox Sheets("測試頁").Name

都會出現相同的"測試頁"的訊息視窗. 集合指名法還有另一個方法是指出集合中的順序, 如

集合指名法2 : Msgbox Sheets(1).Name

順序是依工作表中從左到右的順序為準, 注意, 不是專案視窗中物件的順序. 另外, 集合指名法中的物件集合 Sheets 也可以寫成 Worksheets, 它有兩個名稱, 大概是VBA前後版相容性的結果吧.

  一般程式中大都用直接指名法, 因為只有一個名稱, 真實名稱, 是程式設計師所設定的名稱. 但在VBA的巨集中反而使用集合指名法會多一些, 因為我們會較熟悉工作表的別名, 且在工作自動化的過程中, 可能會用到順序指名, 而在VBA中把物件的陣列功能拿掉了, 就喪失了自動化的能力了.

沒有留言:

張貼留言