You are on page 1of 48

簡介

VBA(全名為 Visual Basic for Applications)是一種 Windows 下的巨集程式


語言,其語法承襲傳統的 Visual Basic,在微軟 Office 之中的各種軟體(如
Word、Excel 等)都可以直接使用,不需要另外安裝。

由於現在的 Windows 個人電腦幾乎每一台都會安裝有微軟的 Office,而 VBA


是附屬在 Office 之下的巨集程式語言,也就是說幾乎每一台電腦都有 VBA 的
開發與執行環境,普及率相當高,只要熟練 VBA 的操作,可以有相當廣泛的應
用。
常用指令

 開啟新檔
Sub open_new_file ()
Workbooks.Add
End Sub

 開起舊檔
Sub open_old_file ()
Workbooks.Open(“D:\... .xlsx”)
End Sub

 使用物件變數方式開啟舊檔
Sub open2 ()
Dim Wkb As Workbook
Set Wkb=Workbooks.Open(FileName:=”… .xlsx”)
End Sub

 截取檔案名稱
Sub catch()
Dim wkb As Workbook
Set wkb = Workbooks.Open(“D:\test.xlsx”)
MsgBox wkb.Name
MsgBox wkb.FullName
MsgBox wkb.Path
End Sub
->執行結果
MsgBox wkb.Name
test.xlsx
MsgBox wkb.FullName
D:\test.xlsx
MsgBox wkb.Path
D:

 將已開啟的活頁簿變為作用中的活頁簿
Sub 啟動活頁簿()
Workbooks(“test.xlsx”).Activate
End Sub

 另存新檔
Sub save_as()
Dim wkb As Workbook
Set wkb=Workbooks.Open(“D:\test.xlsx”)
Wkb.SaveAs Filename:=”test1.xlsx”
End Sub

 直接存檔
Sub save()
ActiveWorkbook.Save
End Sub

 關閉檔案
關閉檔案時會跳出詢問視窗,詢問是否儲存檔案
ActiveWorkbook.Close
直接儲存並關閉
ActiveWorkbook.close SaveChanges:=True
直接關閉但不儲存
ActiveWorkbook.Close SaveChanges:=False

視窗
 啟動視窗
Sub window()
Workbooks(“test.xlsx”).Activate ‘啟動活頁簿
Windows(“test.xlsm”).Activate ‘啟動視窗
End Sub

 最大化或最小化視窗
Sub 視窗調整()
Windows(1).WindowState=xlMaximized ‘最大化
Windows(2).WindowState=xlMinimzed ‘最小化
End Sub

 視窗關閉
Sub close()
Windows(1).close
Windows(“test.xlsx”).close
Workbooks(“temp.xlsm”).close
End Sub

工作表
儲存格、工作表與活頁簿的操作

儲存格

 Range
Range 用來選取要操作的儲存格。
Range(“A1”).Value=”Hi!” ‘將 Hi 字串放入儲存格
Range(“A1:B4”).Value= 123 ‘將每個儲存格填入 123
Range(“A1:A3,D1:E2”).Value=100 ‘可以給動任意範圍

 Cells
除了 Range 之外,Cells 也是一個可以用來操作儲存格的物件,其功能跟
Range 都差不多,只不過它是使用行與列的編號來指定儲存格,這種指定方式
在撰寫自動化程式時會比較好用。
Cells(1,1).Value=123 ‘等同於 Range(“A1”).Value=123

 Range 與 Cells 搭配
Range(Cells(1,1),Cells(3,3)).Value=333
選擇某一個範圍內之儲存格
Range(Cells(1,1),Cells(3,3)).Select
 Rows 與 Colums
若要選取整個欄或列可以使用 Colums 或 Rows

 複製與貼上
Range(“資料範圍”).Select
Selection.Copy
Range(“貼上之位置”).Select
ActiveSheet.Paste
 刪除儲存格內容
1.
Range(“資料範圍”).ClearContents
2.
Range(“資料範圍”)value=””

工作表

 更改工作表中儲存格
以工作表名稱指定工作
Worksheets("工作表 1").Range("A1").Value = "工作表 1 的 A1"
Worksheets("工作表 2").Range("A1").Value = "工作表 2 的 A1"
以工作表順序指定工作表
Worksheets(1).Range("A1").Value = "工作表 1 的 A1"
Worksheets(2).Range("A1").Value = "工作表 2 的 A1"

 新增工作表
Worksheets.Add

 改變工作表名稱
Worksheets(“工作表名稱”or 編號).Name= “新的名稱”

其他功能
 計算工作表數目
Worksheets.Count
活頁簿

 更改活頁簿中某工作表中儲存格
Workbooks("活頁簿名稱"or 編號).Worksheets(“工作表名稱”or 編號).
Range("儲存格").Value = 內容

 改變活頁簿名稱
Workbooks(“活頁簿名稱”or 編號).Name=“新的名稱”

 打開活頁簿
Workbooks.Open "路徑"

 指定活頁簿
Workbooks("名稱").Activate

 活頁簿存檔
Workbooks("名稱").Save

 關閉活頁簿
Workbooks("名稱").Close
*關閉 excel 程式: Application.Quit
變數

變數的宣告與初始值

Dim 變數名稱 As 變數類型


Dim x As Integer
x=123
將變數指定給儲存格
Range(“儲存格”).Value=變數名稱

變數類型

整數-Integer(5, 12,…)
浮點數-Double (1.23…)
布林值-Boolean(True..)
字串-String(“Hellow!”)
時間-Data

未經宣告的變數

Excel VBA 也允許使用者省略變數宣告,預設會是 Variant 萬用類型,所以其


實未經宣告也可以直接定義變數,但是不嚴謹。
Var1 = 123 ‘是允許的
**建議編寫程式時,變數都應明確宣告,以免變數名稱誤植造成的 bug。若想
避免自己在寫程式時,不小心在沒有宣告的情況下就使用變數,或是有宣告變
數,但是在使用時打錯又沒發現,可以在程式碼的開頭加上 Option Explicit,
之後只要遇到變數未宣告就使用的狀況,編譯時就會出現錯誤訊息。

萬用型變數

萬用類型變數,也就是一種可以儲存任何資料的類型,其稱為 Variant,其使
用方式與一般的類型類似。
Dim x As Variant
x = "G.T.Wang"
MsgBox "My name is " & x
x = 123
MsgBox "The value is " & x
如果在宣告變數時不指定變數類型,則 VBA 預設會將變數視為 Variant 類型,
所以這兩種寫法的效果是相同的。
Dim x As Variant
Dim x ' 預設為 Variant
**Variant 類型的變數通常可用於從 Excel 儲存格中讀取使用者輸入的資料,由
於使用者輸入的資料變異性很大,可能會是任何類型,所以直接使用 Variant
來儲存會比較方便。
雖然 Variant 非常方便,但它的缺點就是程式執行效能較差,所以除非必要,
在一般的情況下還是使用固定的變數類型較佳。

變數範圍

 區域變數
在一般的 Sub 副程式中宣告的變數,其範圍僅限於該副程式,也就是所謂的區
域變數。
 全域變數
若希望一個變數可以在整個模組內的副程式中使用,則可將變數宣告於副程式
之外,建立模組層級的變數。
 跨模組變數
如果在程式比較複雜時,一個應用程式中可能會有好多個模組,一般模組層級
的變數只能在該變數隸屬的模組之內被存取,若要在其他模組之內也可以存取,
就必須將變數宣告為公開(public)的變數,宣告方式是將 Dim 改為 Public。

常數

如果希望變數的內容在程式的執行過程中都是固定的,不會不小心被更改,將
Dim 改成 Const,即宣告為常數。
運算子

算數運算子

運算子 說明 優先順序
^ 冪運算(次方) 1
* 乘法 2
/ 除法 2
\ 整數除法 3
Mod 餘數 4
+ 加法 5
- 減法 5

字串運算

運算子 說明
& 串接兩個字串
Text1=”Hi!~”&”Mason”

比較運算子

運算子 說明
= 等於
<> 不等於
< 小於
> 大於
<= 小於或等於
>= 大於或等於

邏輯與位元運算子

運算子 說明
And AND 運算
Or OR 運算
Not NOT 運算
Xor XOR 運算
Dim a, b, c, x, y, z As Integer
a = 10
b=8
c=6
x = (a And b) ' 結果為 8
y = (a And c) ' 結果為 2
z = (b And c) ' 結果為 0
MsgBox "x = " & x & ", y = " & y & ", z = " & z
條件判斷

如果希望電腦依據設定的條件判斷後做出不同的結果,就會需要使用條件判斷
的語法。

IF THEN ,ELSE

IF 條件判斷式 THEN
條件成立時執行
ELSE
條件不成立時執行
END IF
Dim grade As Integer
grade= 92 ' 分數
If grade > 60 Then
MsgBox "及格!"
Else
MsgBox "當掉!!"
End If

IF THEN, ELSE IF

IF 條件判斷式 1 THEN
條件 1 成立時執行
ELSEIF 條件判斷式 2 THEN
條件 2 成立時執行
ELSE
都不成立時執行
END IF
Dim grade As Integer
grade= 92 ' 分數
If grade > 60 Then
MsgBox "及格!"
Elseif grade >50 Then
MsgBox "可補考!!"
Else
MsgBox "當掉!!"
End If

**If Then 的條件判斷式在執行時只會選擇第一個符合的條件來執行,而且在判


斷條件時是有順序性的,以這個例子來說,首先會判斷第一個 If 條件是否成立,
如果成立的話就會執行對應的動作,然後其他的 ElseIf 就不會再繼續判斷或執
行了。
**程式在執行時會依照順序一個接著一個檢查每一個條件,找到符合的條件就
會執行對應的動作,如果找到最後都沒有任何一個條件符合,就會執行 Else 的
對應動作。
**如果在條件不成立的時候,不需要執行任何動作,就可以把 Else 拿掉。

SELECT CASE

Select Case 變數或運算式


Case 數值 1 或 某數值區間 或 多個數值與值範圍的組合
符合數值 1 時執行
Case 數值 2 或 某數值區間 或 多個數值與值範圍的組合
符合數值 2 時執行
…依此類推
Case Else
都不符合時執行
End Select
**每個 Case 陳述式可以包含多個值、 一個範圍的值或與比較運算子的組合的
值。(ex. Case 1 To 4, 7 To 9, 11, 13, Is >給訂的範圍值)
** Is 比較運算子和用於 Select Case 陳述式的 Is 關鍵字並不相同。

Dim Number
Number = 8 ' Initialize variable.
Select Case Number ' Evaluate Number.
Case 1 To 5 ' Number between 1 and 5, inclusive.
Debug.Print "Between 1 and 5"
' The following is the only Case clause that evaluates to True.
Case 6, 7, 8 ' Number between 6 and 8.
Debug.Print "Between 6 and 8"
Case 9 To 10 ' Number is 9 or 10.
Debug.Print "Greater than 8"
Case Else ' Other values.
Debug.Print "Not between 1 and 10"
End Select

Function Bonus(performance, salary)


Select Case performance
Case 1
Bonus = salary * 0.1
Case 2, 3
Bonus = salary * 0.09
Case 4 To 6
Bonus = salary * 0.07
Case Is > 8
Bonus = 100
Case Else
Bonus = 0
End Select
End Function
迴圈控制

迴圈控制是各種程式語言都會有的基本功能,他的作用是可以讓電腦重複執行
某一段類似的動作,在運用得當的情況下,可以幫助使用者快速處理大料的資
料,既省時又省力。

FOR

For Loop 迴圈主要用於已知重複次數的問題,也就是在執行迴圈之前,就已經


事先知道要迭代幾次。
FOR 計數之變數=開始值 TO 結束值 (STEP 每次遞增或遞減的值)
執行之程式碼
NEXT 計數之變數
Dim i, s As Integer
s=0
For i = 1 To 10
s=s+i
Next i
MsgBox "s = " & s

Dim i, s As Integer
s=0
For i = 1 To 10
s=s+i
If i >= 4 Then
Exit For ' 中斷迴圈
End If
Next i
MsgBox "s = " & s
**Exit For: 如果想要中途跳出迴圈,可以加上 Exit For 來中斷迴圈。

For i = Range("A1").End(xlDown).Row To 4 Step -3


Rows(i).Delete
Rows(i - 1).Delete
Next

FOR EACH

For Each 迭代變數 In 陣列或範圍內儲存格


運算內容
Next 迭代變數
For Each 迴圈是 For Loop 迴圈的另外一種精簡寫法,兩者功能差不多,只是
在某些狀況下使用 For Each 迴圈會比較方便。
一般若要使用 For Loop 迴圈對陣列中的每個元素逐一處理時,必須明確指定
開始與結束的索引值,再以索引值取出陣列中的元素做進一步的運算,而 For
Each 迴圈則是可以自動將陣列中的元素逐一取出,放進迴圈的迭代變數中處理,
這樣的寫法會比 For Loop 更精簡。
**Exit For: 如果想要中途跳出迴圈,可以加上 Exit For 來中斷迴圈
Dim wSheet As Worksheet
For Each wSheet In Worksheets
MsgBox "找到工作表: " & wSheet.Name
Next wSheet

DO WHILE

Do While 條件判斷式
運算內容
Loop
Do 迴圈可以重複執行某一段程式碼,直到指定的條件判斷式成立或是不成立的
時候,才停止迴圈的執行。
Do 條件判斷式
運算內容
Loop While
While 的條件判斷式也可以放在迴圈結尾處,這樣的話迴圈在執行時就會先執
行第一次的迭代,執行完第一次之後才判斷是否要繼續執行下一次的迭代,如
果條件成立則繼續執行,若不成立就立刻中止迴圈,這樣的寫法只是檢查條件
判斷式的時機不同,觀念上大同小異。
** Exit Do:欲在 Do Loop 迴圈執行到一半時終止迴圈的執行,可以在迴圈中加
入 Exit Do 用來跳出迴圈。
Dim i, s As Integer
s=0
i=1
Do While i <= 10
s=s+i
i=i+1
Loop
MsgBox "s = " & s
DO UNTIL

Do Until 條件判斷式
運算內容
Loop
Do Until Loop 迴圈與 Do While Loop 迴圈在條件的判斷上剛好相反,Do
Until Loop 迴圈會在指定條件不成立(判斷為 False)時繼續執行,當判斷條
件成立時(判斷為 True)則終止迴圈。
Do 條件判斷式
運算內容
Loop Until
Do Until Loop 迴圈也可以將條件判斷式放在迴圈的最後面,這跟上面 Do
While Loop 迴圈也很類似。
Dim i, s As Integer
s=0
i=1
Do Until i > 10
s=s+i
i=i+1
If i > 4 Then
Exit Do ' 中斷迴圈
End If
Loop
MsgBox "s = " & s

Dim i, s As Integer
s=0
i=1
Do
s=s+i
i=i+1
Loop Until i > 10
MsgBox "s = " & s
其他結構

WITH

With 對象
.[属性 1] = 屬性值
.[属性 2] = 屬性值

End with
VBA 中,With 結構用於組合同一個對象的多個屬性和方法,避免重複寫同一
個對象名稱,提高程式撰寫效率與簡潔性。

在圖表中新增其他曲線
ActiveSheet.ChartObjects("圖表 1").Activate

With ActiveChart.SeriesCollection.NewSeries
.XValues = Range("A15:a20")
.Values = Range("b15:b20")
.Name = Range("a14").Value
End With
以上等同於
ActiveChart.SeriesCollection.NewSeries .XValues = Range("A15:a20")
ActiveChart.SeriesCollection.NewSeries .Values = Range("b15:b20")
ActiveChart.SeriesCollection.NewSeries .Name = Range("a14").Value
字串處理

字串的宣告與定義

Dim mystr As String


mystr = "This is a message!!!"

連接字串

要將多個字串連接起來,可以使用 & 這個運算子。


Dim text1 As String, text2 As String
text1 = "Hello"
text2 = "World"
MsgBox text1 & ", " & text2

其他字串相關的函數

字串建立函數

STRING 函數
用來建立重複字元的字串。
Dim text1, text2 As String
text1 = String(6, "A") '產生 AAAAAA
text2 = String(3, 100) '產生 ddd (100 就是指 d 的 ASCII 編碼)

字串截取函數

Len 函數
可以用來取得一個字串的長度。

INSTR 函數
INSTR(母字串, 子字串)
用來尋找一個子字串位於某個字串的位置。
Dim pos As Integer
pos = InStr("Hello, world.", "world")
MsgBox "pos = " & pos
**InStr 預設在搜尋時,英文的大小寫是視為不同的,若希望不分大小寫,可將
比對方式參數設定為 vbTextCompare。
Dim pos As Integer
pos = InStr(1, "Hello, World.", "world", vbTextCompare)
MsgBox "pos = " & pos
**這裡的第一個參數是用來指定搜尋的起始位置,在上面的範例中我們將這個
參數省略掉了,在這裡使用比對方式參數時就要加進來,這樣執行之後 InStr
在比對字串時就會將英文字母的大小寫視為相同的。

LEFT 函數
LEFT(字串, 截取長度)
從左邊開始擷取出子字串,第一個參數是完整的字串,而第二個參數則是要擷
取的子字串長度。
**RIGHT 則功能相反,其由右邊開始截取字串。

MID 函數
MID(字串, 左起第幾個位置開始, 截取長度)
MsgBox Mid("This is a message.", 6, 2) ‘is
**如果使用 Mid 時不指定擷取的字串長度,則 Mid 就會從指定的位置開始擷
取直到整個字串的結尾。

字串分割函數

SPLIT 函數
Split 函數其傳回的結果就是一個一維的陣列,將分割的字串分別放入陣列內。
Dim arr As Variant
Dim ub As Integer

' 使用 Split 以空白字元分割字串


arr = Split("This is a test", " ")

' 取得陣列長度
ub = UBound(arr)

' 輸出陣列內容
For i = 0 To ub
MsgBox (i & " = " & arr(i))
Next i

刪除字串空白字元函數

LTRIM 函數
LTRIM(字串)
用來移除左邊開頭所有的空白。
Dim mystr As String
mystr = " Hello, world."
MsgBox "After LTrim : " & LTrim(mystr)

RTRIM 函數
功能則是移除字串右邊所有的空白。
TRIM 函數
功能是移除開頭與結尾的所有空白。

SPACE(數字)
插入想要多少空白

字串取代函數

Replace 函數
Replace(字串, 被替換文字, 用來替換之文字[, 起始位置[, 替換次數[, 比對方
式]]])
Dim mystr As String
mystr = "This is a message."
newstr = Replace(mystr, "message", "dog")
MsgBox "After Replace : " & newstr

Dim mystr As String


mystr = "This is a message."
newstr = Replace(mystr, "is", "**", 5, 1)
MsgBox "After Replace : " & newstr ‘OUTPUT: After Replace: ** a message.
**如果要替換比較後面的文字,可以調整起始位置參數,不過 Replace 會自動
將起始位置之前的文字截斷。
**預設的狀況下 Replace 會將英文字母的大小寫視為不同的字串。若在比對時,
不要區分英文字母的大小寫(大小寫視為相同),可以將比對方式參數指定為
vbTextCompare。

字串比較函數

STRCOMP 函數
STRCOMP(字串一, 字串二, [比對方式])
前兩個參數就是兩個要比較的字串,而第三個比對方式參數可用來指定是否區
分 大 小 寫 , 預 設 值 是 vbBinaryCompare ( 大 小 寫 視 為 不 同 ) , 若 設 定 為
vbTextCompare 則會將大小寫視為相同。
狀況 傳回數值
字串一 < 字串二 -1
字串一 = 字串二 0
字串一 > 字串二 1

字串搜尋函數

INSTR 函數
INSTR(目標字串, 欲搜尋之子字串)

字串反轉
STRREVERSE 函數
STRREVERSE(字串)
功能為將字串順序顛倒。

VAL 函數
VAL(字串)
功能為將字串轉成數值
陣列

一維陣列

Dim 陣列名稱(陣列的結尾索引值) As 陣列的變數型態


**VBA 的陣列索引預設是從 0 開始的依序遞增至陣列的結尾索引值。
**沒有指定陣列類型的話,預設就是 Variant。

多維陣列

Dim 陣列名稱(陣列的結尾索引值, 陣列的結尾索引值..) As 陣列的變數型態


Dim mat(2, 3) As Integer ‘a 3X4 array
‘initial value
mat(0, 0) = 2
mat(2, 3) = 5

自訂索引範圍

某些時候索引值由 0 開始會使得陣列處理起來有些綁手綁腳,若要改變陣列索
引的起始值,可以自行指定索引範圍的開始與結束值。
Dim MyArray2(1 To 3) As Integer ‘MyArray(1), MyArray(2), MyArray(3)
陣列初值給定

Array 函數可以讓設計者輸入一連串的資料,直接建立一個陣列,這種寫法對
於初始化一些包含常數的靜態陣列非常有用。
Dim arr As Variant
arr = Array(1, 2, 3)
MsgBox (arr(1))

動態陣列

一般的陣列在宣告時就必須決定好陣列的大小,但是有的時候我們無法事先預
知程式所需要的陣列大小,要等到實際執行時才會知道需要多少空間,這時候
就可以使用動態陣列的方式來儲存資料。
動態陣列(dynamic arrays)的特點就是可以動態改變陣列的大小,在空間不
足時可以擴增,而空間太大時也可以縮減。
' 宣告動態陣列
Dim 陣列名稱() As Integer
' 調整陣列大小
ReDim 陣列名稱(陣列的結尾索引值)
**釋放記憶體: 當動態陣列使用完畢之後,我們可以使用 Erase 將系統配置給動
態陣列的記憶體收回。
Erase 陣列名稱
** LBound 與 UBound 是用來查詢陣列索引下限與上限的函數。
其他應用

分割字串

在 VBA 中若要將一串文字分割成好多段,可以使用 Split 函數,而其傳回的結


果就是一個一維的陣列。
Dim arr As Variant
Dim ub As Integer

' 使用 Split 以空白字元分割字串


arr = Split("This is a test", " ")

' 取得陣列長度
ub = UBound(arr)

' 輸出陣列內容
For i = 0 To ub
MsgBox (i & " = " & arr(i))
Next i
或是我們也可以使用 For Each 的方式來處理陣列,這樣就可以省去取得陣列長
度的步驟。
' 使用 For Each 輸出陣列內容
For Each s In arr
MsgBox (s)
Next s
若要將陣列的所有元素串接成一個字串,可以使用 Join 函數。
Dim str As String

' 使用 Join 將陣列內容串接為一個字串


str = Join(arr, " ")

MsgBox (str)
篩選陣列元素

Filter 函數可以篩選陣列的每個元素,將符合條件的元素拿出來,組成新的陣列
並傳回。
Dim arr, flt As Variant

' 建立測試用陣列
arr = Array("ABC", "BCD", "CDE")

' 篩選出含有 "B" 的元素


flt = Filter(arr, "B")

' 輸出結果
For Each x In flt
MsgBox ("result: " & x)
Next x
取得陣列元素位置

Dim arr, pos As Variant

' 建立測試用陣列
arr = Array("ABC", "BCD", "CDE")

' 尋找 "BCD" 的位置


pos = Application.Match("BCD", arr, 0)
函數(Function)與子程式(Subroutine)

子程式

 VBA 的 Sub 與 Function 類似,可傳入各種參數並進行運算,但是沒有傳


回值(沒有辦法傳回計算結果)。
Sub 子程式名稱( [Optional] 變數名稱 As 變數型態, …)
子程式的內容
End Sub
呼叫子程式
子程式名稱 參數 1,參數 2,…

子程式的參數

 子程序是以 Sub 這個關鍵字加上一個子程序名稱開始的,子程序名稱後方


會接著一對小括號,小括號內部會放置傳入的參數(這個例子中沒有任何
傳入參數),而最後面的 End Sub 就是子程序的結尾,中間的部分就是子
程序的程式內容。
 定義好的子程序之後,就可以在程式的其他地方使用它,呼叫子程序時只
要直接輸入子程序,再依序加上逗點分隔的輸入參數即可。
 在定義子程序(Sub)的參數時,我們可以透過 Optional 這個關鍵字將某
些參數設定為選擇性的,並且加上參數的預設值
Sub hello()
mySub 1, 2 ' 呼叫 mySub 子程序
mySub_B ' 計算 3 + 4
mySub_B 1 ' 計算 1 + 4
mySub_B 1, 2 ' 計算 1 + 2
End Sub

Sub mySub(x As Integer, y As Integer)


MsgBox ("x + y = " & x + y)
End Sub

Sub mySub_B(Optional x As Integer = 3, Optional y As Integer = 4)


MsgBox ("x + y = " & x + y)
End Sub

傳值與傳參考呼叫

 在預設的情況下,如果我們在子程序改變的傳入參數的值,原來的值也會
跟著改變,也就是 VBA 為傳址呼叫(call by reference)。如果不想要讓
子程序去改變原本的變數值,可以改用傳值呼叫(call by value)的方式
來定義子程序的參數。(在子程式參數前加上 ByVal)
Sub mySub3(ByVal x As Integer) ‘call by value
x=x+1
End Sub

Sub mySub3(ByRef x As Integer) ‘call by reference


x=x+1
End Sub

函數

VBA 的 Function 就像一般程式語言的函數,可傳入各種參數,進行自訂的運


算,並將計算結果傳回。與子程序(Sub)不一樣的地方是函數在執行完之後
會有一個傳回值,而子程序則沒有。
Fuction 函數名稱([Optional] 變數名稱 As 變數型態[=value],...) As 函數的變數
型態
函數的內容
End Function

Sub test()
Dim y as integer
y=sum(5,9)
End Sub
Function sum(x1 As Integer, x2 As Integer) As Integer
Sum=x1+x2
End Function

函數的參數

 函數的預設參數值用法與子程序相同,同樣都是使用 Optional。
Function myFun2(Optional x As Integer = 3, Optional y As Integer = 4) As Integer
myFun = x + y
End Function

傳值與傳參考呼叫

 函數的傳值與傳參考呼叫原則與子程序相同,傳值使用 ByVal 而傳址使用


ByRef。
範例

 將選定範圍內所有偶數加總
Function SumEvenNumbers(r As Range) As Integer
Dim c As Range
SumEvenNumbers = 0
' 將範圍內的每一個儲存格資料取出
For Each c In r
' 檢查是否為偶數
If c.Value Mod 2 = 0 Then
' 將偶數加總起來
SumEvenNumbers = SumEvenNumbers + c.Value
End If
Next c
End Function
時間

日期與時間是一種比較特別,但是也時常會被使用到的資料類型,在 VBA 中我
們可以使用 Date 這種特殊的變數類型來儲存日期與時間的資料,同時 VBA 中
也內建了許多用來處理日期與時間的工具函數。

設定日期

 VBA 中的 Date 變數是一種專門用來儲存日期與時間的變數,再配合使用


DateValue 函數來設定日期或直接使用字串。
Dim d As Date
d = DateValue("Jun 23, 2020") ‘ also d=” Jun 23, 2020”
 DateValue 可接受的日期格式有好多種,如以下格式範例:
d=DataValue(“2020/01/23”)
d=DataValue(“2020-01-23”)
d=DataValue(“23-JAN”)
d=DataValue(“23-JAN-2020”)
d=DataValue(“01/23/2020”)
 DateSerial 函數可以接受數值的年、月、日,建立日期變數
Dim d As Date
d = DateSerial(2016, 5, 10)

設定時間

 如同日期設定,時間的設定則可使用 TimeValue 與 TimeSerial 函數。


Dim t As Date
t = TimeValue("20:15")
MsgBox (t)
t = TimeSerial(20, 15, 20)
MsgBox (t)

取得現在時間

 Date 函數可以取得系統上現在的日期,Time 函數會傳回系統目前的時間,


而 Now 函數則會傳回系統上目前的日期與時間。
Dim d As Date

' 現在日期
d = Date
MsgBox ("現在是:" & d)

' 現在時間
d = Time()
MsgBox ("現在是:" & d)

' 現在日期與時間
d = Now()
MsgBox ("現在是:" & d)
**Timer 函數可以傳回從當天 12:00 AM 到目前的秒數,精確度可以到千分之一
秒。

日期與時間的運算

 DateAdd 函數可以計算日期或時間的加減法運算,算出某段時間之後的時
間點。
Dim d As Date

d = "01-Jan-2013"
MsgBox ("一年後:" & DateAdd("yyyy", 1, d))
MsgBox ("一季後:" & DateAdd("q", 1, d))
MsgBox ("一月後:" & DateAdd("m", 1, d))
MsgBox ("一天後:" & DateAdd("d", 1, d))
MsgBox ("一週後:" & DateAdd("ww", 1, d))

d = "01-Jan-2013 12:00:00"
MsgBox ("一小時後:" & DateAdd("h", 1, d))
MsgBox ("一分鐘後:" & DateAdd("n", 1, d))
MsgBox ("一秒鐘後:" & DateAdd("s", 1, d))

 DateDiff 函數可以計算兩個時間點之間的時間間隔。
Dim d1, d2 As Date
d1 = "2017/10/02"
d2 = "2018/11/14"

MsgBox ("相差年: " & DateDiff("yyyy", d1, d2))


MsgBox ("相差季: " & DateDiff("q", d1, d2))
MsgBox ("相差月: " & DateDiff("m", d1, d2))
MsgBox ("相差日: " & DateDiff("d", d1, d2))
MsgBox ("相差週: " & DateDiff("ww", d1, d2))

d1 = "01-Jan-09 00:00:00"
d2 = "01-Jan-10 23:59:00"
MsgBox ("相差小時: " & DateDiff("h", d1, d2))
MsgBox ("相差分鐘: " & DateDiff("n", d1, d2))
MsgBox ("相差秒鐘 : " & DateDiff("s", d1, d2))

取出部分日期或時間

取出目標 函數
年 Year(變數)
月 Month(變數)
日 Day(變數)
星期 Weekday(變數)
時 Hour(變數)
分 Minute(變數)
秒 Second(變數)
**Weekday 預設會傳回 1 到 7 的整數,分別代表星期日到星期六,如果要取
得星期名稱,可再使用 WeekdayName 函數轉換,而月份名稱也是用類似的
方式,使用 MonthName 函數轉換。
WeekdayName(Weekday(變數))
MonthName(Month(變數))

日期與時間輸出格式

 FormatDateTime 函數可依據指定的格式輸出日期與時間,可用的輸出格
式如下:
FormatDateTime(變數, 格式代碼)
格式代碼 說明
0 或 vbGeneralDate 完整格式(預設值)
1 或 vbLongDate 完整日期
2 或 vbShortDate 簡短日期
3 或 vbLongTime 完整時間
4 或 vbShortTime 簡短時間
檔案的輸入與輸出

檔案讀入

 通常 Excel VBA 程式都會從 Excel 表格中取得資料,經過處理之後再送回


Excel 表格中,但有些時候我們也會需要直接從外部的文字檔讀取資料,或
是將處理完的資料儲存至外部的文字檔中,以下是 Excel VBA 中檔案讀取
與寫入的教學。
 開啟檔案之後,通常我們會使用迴圈的方式來讀取整個檔案內容,每次以
Line 讀取一行資料,再將資料存入字串變數中,然後藉由 EOF(end of
file)來判斷檔案是否已經讀取到結尾,如果讀取至結尾則跳出迴圈
' 開啟,使用編號 #num 檔案代碼
Open “檔案路徑” For Input As #數字
Do Until EOF(檔案編號) ' 執行迴圈,直到編號 #num 檔案遇到結尾為止
Line Input #數字, 字串變數 ' 從編號 #1 檔案讀取一行資料
其他程式碼
Loop
Close #數字 ' 關閉編號 #1 檔案
檔案寫出

 這個語法跟之前讀檔的語法類似,只是把開檔的模式改為 Output,檔案
位置就填入要寫入的檔案路徑,而檔案代碼也是自己取一個不重複的數字
即可。開啟檔案之後,即可使用 Print 函數將指定的資料寫入檔案中。
' 開啟文字檔,使用編號 #num 代表檔案代碼
Open “檔案路徑” For Output As #檔案編號

Print #檔案編號, 寫出之內容 ‘變數或字串

Close #檔案編號 ' 關閉編號檔案


 Print 的使用:
使用分號(;)來連接字串。
 除了使用 Print 之外,還有一個 Write 也可以寫入檔案,它會自動將文字
的內容加上雙引號,並以逗號分隔多個輸入變數。

附加方式寫入檔案

 當我們用 Output 模式寫入一個已經存在的檔案時,如果檔案中存在有舊


的內容的話,在寫入之後就會將舊的內容覆蓋掉,若想要保留舊資料,並
增加一些內容接在原本資料的後方。使用時只要將檔案模式改成 Append
即可,最典型的使用情境就是將程式輸出訊息寫入記錄檔。

自動選擇檔案代碼

如果要使用程式自動開啟多個檔案,不想使用手動指定檔案代碼的話,可以使
用 FreeFile 這個函數自動取得可用的檔案代碼。
Dim FileCount As Integer
Dim FileNumber As Integer
Dim FileName As String
' 使用迴圈自動寫入 5 個檔案
For FileCount = 1 To 5

' 自動取得檔案代碼
FileNumber = FreeFile()

' 檔案名稱
FileName = "C:\ExcelDemo\demo_output_" & FileCount & ".txt"

' 開啟檔案
Open FileName For Output As #FileNumber

' 寫入檔案
Print #FileNumber, "Hello, World"

' 關閉檔案
Close #FileNumber

Next FileCount
事件

 當 使 用 者 在 Excel 中 進 行 某 些 特 定 的 操 作 時 , 就 會 觸 發 所 謂 的 事 件
(events),例如當使用者選擇一張工作表時,就會觸發工作表選擇的事
件,而像點選儲存格或儲存檔案等動作也都會觸發對應的事件。
 程式設計者可以靠著各種事件的的觸發來設計可以自動執行的 VBA 程式,
例如在選擇工作表時,自動執行某些 VBA 程式等。以下介紹如何在 Excel
VBA 中使用事件。

使用說明

STEP 1
在工具列中點選「Visual Basic」,開啟 Visual Basic 編輯器,或是直接使用快
速鍵 Alt + F11 開啟。

STEP 2
如果要處理活頁簿的事件,就要把事件處理子程序寫在活頁簿中,若是要處理
單張工作表的事件,就放在對應的工作表中。這裡我們以活頁簿事件來作為示
範,點擊兩下活頁簿,開啟程式碼編輯視窗。

STEP 3
選擇活頁簿(Workbook)。
如果只要處理特定工作表中的儲存格變更事件,也可以把事件處理子程序寫在
對應的工作表中。

STEP 4
這時候程式碼編輯視窗內會出現一個預設的 Workbook_Open 處理子程序,這
個就是對應到活頁簿開啟事件(open event)的處理子程序,寫在這裡的程式
碼就會在活頁簿開啟時自動執行。
在右上角的下拉式選單中可以選擇其他的事件,建立不同事件的處理子程序。
這個例子我們先以 Workbook_Open 處理子程序做為示範。
STEP 5
在 Workbook_Open 子程序中,填入當活頁簿開啟時所要執行的程式碼,這裡
我以一行簡單的 MsgBox 輸出訊息作為示範。

STEP 7
存檔之後,先將 Excel 檔案關閉,再重新打開,而開啟時 Workbook_Open 處
理子程序中的 VBA 程式就會自動被執行,所以就會看到我們剛剛寫的提示訊息。
參考資料

1. https://blog.gtwang.org/programming/vba/

You might also like