Excel VBA


### 概要・用語
Excelを自動化するためのプログラミング言語

* オブジェクト
* ワークブック
* ワークシート
* セル
* プロパティ(属性): フォント、色
* メソッド(処理): セルの削除、挿入

### Visual Basic Editor
ツール -> マクロ -> Visual Basic Editor
プロパティウインドウ
プロジェクト、挿入、標準モジュール

### Hello World
実行すると「Hello World」のメッセージボックスを出力する

```vbnet
Sub HelloWorld() ' 開始にはSubが必要
  MsgBox("hello world")
End Sub

Sub HelloWorld2()
  MsgBox _
  ("hello world2") ' _を付けると一行と認識
End Sub
```

### セルの値の表示
```vbnet
Sub CellChange()
  Worksheets("Sheet1").Range("A1").Value = "hello"
  Range("A2").Value = "hello2"
  Cells(3, 1).Value = "hello3"
  Cells(3, 1).Offset(1, 0).Value = "hello4" ' 相対的な位置指定
End Sub
```

```vbnet
Sub CellChange()
  Range("A1", "B3").Value = "hello" ' 範囲の指定方法
  Range("A5:C7").Value = "hello2" ' 範囲の指定方法2
  Range("4:4") = "row4" ' 4行目全てという意味
  Range("C:C") = "columnC" ' C列全てという意味
  Cells.Clear ' セルが全てクリアされる
End Sub
```

### With
```vbnet
' Range("A1")が何度も出ていて冗長
Sub WithTest()
  Range("A1").Value = "hello"
  Range("A1").Font.Bold = True
  Range("A1").Font.Size = 16
  Range("A1").Interior.Color = vbRed
Ebd Sub
```

```vbnet
' 何度も書かなくて良くなる
Sub WithTest()
  With Range("A2")
    .Value = "hello"
    With Font
      .Bold = True
      .Size = 16
    End With
    .Interior.Color = vbRed
  End With
Ebd Sub
```

### 値の取得
```vbnet
Sub GetTest()
  MsgBox(Range("A1").Value)
  MsgBox(Range("A1").Font.Size)
Ebd Sub
```

### メソッド
```vbnet
Sub MethodTest
  ' B2の値を消去する
  Range("B2").Clear
  
  ' B5を消して全体を1つ上につめる
  ' shift:=xlShiftUpはオプション
  Range("B5").Delete shift:=xlShiftUp
  
  ' Sheet2の後ろに2枚シートを追加する
  ' after、countはオプション
  Worksheets.Add after:=Worksheets("Sheet2"), count:=2
End Sub
```

### VBAとマクロの関係
VBAにコードを書く = Excelのマクロを作る
マクロを作る = VBAのコードを生成する

### 変数を使用
```vbnet
Sub VariableTest()
  Dim x As Integer ' 変数の宣言
  x = 10
  Range("A1").Value = x
  Debug.Print x ' イミディエイトウィンドウに表示される
  
  ' x = 10 + 5
  ' x = x + 1
  ' + = / * mod ^  
End Sub
```

```vbnet
Sub VariableTest()

 Dim y As Double
 Dim s As String
 Dim d As Date
 Dim z As Variant ' 何かデータが入って初めて型が決まる
 Dim f As Boolean
 Dim r As Range 
 
 y = 10.5
 s = "hello"
 d = "2012/04/23"
 f = True
 Set r = Range("A1") ' Setが必要

 r.Value = d + 7 ' A1に2012/04/30が入る
End Sub
```

### 配列の利用
```vbnet
Sub VariableTest()
  Dim sales(2) As Integer
  sales(0) = 200
  Sales(1) = 150
  Sales(2) = 300
  
  Debug.Print sales(1) ' 150が表示される
End Sub
```

```vbnet
Sub VariableTest()
  Dim sales As Variant
  sales = Array(200, 150, 300)
End Sub
```

### If文
```vbnet
Sub IfTest()
  If Range("A1").Value > 80 Then
    Range("A2").Value = "OK"
  ElseIf Range("A1").Value > 60 Then
    Range("A2").Value = "SoSo"
  Else
    Range("A2").Value = "NG"
  End If

  ' = > < => <= <> and not or
End Sub
```

### Select文
```vbnet
Sub SelectTest()
  Dim signal As String
  signal = Range("A1").Value
  
  Dim result As Range
  Set result = Range("A2") ' Setを忘れない

  Select Case signal
  Case "red"
    result.Value = "STOP"
  Case "green"
    result.Value = "GO"
  Case "yellow"
    result.Value = "CAUTION"
  Case Else
    result.Value = "N/A"
  End Select 
End Sub
```

### ループ処理

* While文

```vbnet
Sub WhiteTest()
  Dim i As Integer
  i = 1
  
  Do While i < 10
    Cells(i, 1).Value = i
    i = i + 1
  Loop
End Sub
```

* For文

```vbnet
Sub ForTest()
  Dim i As Integer
  
  For i = 1 To 9
    Cells(i, 1).Value = i
  Nest i
End Sub
```

* Each文

```vbnet
Sub EachTest()
  Dim names As Variant
  names = Array("taguchi", "fkoji", "dotinstall")
  
  For Each Name in names
    Debug.Print Name
  Nest Name
End Sub
```

### Call

* Subプロシージャー: 返り値を返さない

```vbnet
Sub CallTest()
  Dim names as Variant
  names = Array("taguchi", "fkoji", "dotinstall")
  
  For Each Name in names
    Call SayHi(Name) ' 別のプロシージャーを使用する
  Nest Name  
End Sub

Sub SayHi(ByVal name As String) ' 引数を書く
  Debug.Print "hi, " & name 
EndSub
```

* Functionプロシージャー: 返り値を返す

```vbnet
Sub CallTest()
  Dim names as Variant
  names = Array("taguchi", "fkoji", "dotinstall")
  
  For Each Name in names
    Debug.Print SayHi(Name)
  Nest Name  
End Sub

Function SayHi(ByVal name As String)
  SayHi = "hi, " & name
EndSub
```

### 総復習
以下のようなテーブルがあった時に成績が低いセルを出力する

|name|score|
|:---|:---|
|taguchi|52|
|fkoji|82|
|dotinstall|99|

```vbnet
Sub FindLowScores()
  Dim i As Long
  Dim n As Long
  i = 2
  n = 0
  
  For While Cells(i, 1).Valie <> ""
    If Cells(i, 2).Value < 60 Then
      Cells(i, 2).Interior.Color = vbRed
      n = n + 1
    End If
    i = i + 1
  Loop
  
  MsgBox(n & "件該当しました")
End Sub
```