昨日までJavaJavaしてた人がいきなりExcelのVBAを実装する羽目になったときのためのメモ

Excel は滅びぬ！ Excel の力こそ日本企業の夢だからだ！

VBA 実装してて学んだこととかのメモ。
JavaJava してたかはあまり関係ないかも。

#エディタの使い方
##エディタを表示する
`Alt + F11` で VB エディタを表示できる。

##環境設定
###背景色・フォントを調整する
デフォルトの白背景とか気が狂うので、暗い色にする。

「ツール」→「オプション」を選択し、「エディターの設定」タブを開く。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/b0dee126-fd93-02d7-07ee-7b8a61a719a1.jpeg)

「コードの表示色」を選択して、「背景」の色を選択する。
ついでにフォントも見やすいやつに変更する。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e352e61d-eab3-58ab-0afa-2e3e4eac1638.jpeg)

これだけで開発効率が５割増しになる。

###イミディエイトウィンドウ
####イミディエイトウィンドウを表示する
いわゆるコンソールに当たるのが、__イミディエイトウィンドウ__と呼ばれるウィンドウ。

`Ctrl + G` で表示される。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/5315af60-53fa-8bc9-b102-0fa8a8e6107b.jpeg)

####イミディエイトウィンドウに出力する

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d3f9c1ab-c8b6-0ba5-dad2-c39b0f9236a9.jpeg)

↓イミディエイトウィンドウに実行するプロシージャを入力して、 Enter を入力する。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/20975b45-f540-f557-bf57-e9d70ed8b62b.jpeg)

プロシージャが実行されて、 `Debug.Print` で指定した文字列がイミディエイトウィンドウに出力される。

このように、イミディエイトウィンドウではプログラムの実行とコンソール出力の確認ができる。

デバッグの時は、デバッガで処理を止めながらイミディエイトウィンドウで変数の値やプロシージャの処理結果などをチェックしたりするので重宝する。

###コンパイルエラー時にポップアップが出ないようにする
標準だと、ちょっとでもコンパイルエラーがあれば即座にポップアップが表示されて非常に鬱陶しい。

「ツール」→「オプション」を選択し、「編集」タブの「コードの設定」にある「自動構文チェック」のチェックを外す。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/3d6d631f-17e4-a1ce-e3d2-4c3b0befbd18.jpeg)

これで、ポップアップは表示されなくなる。

###変数の宣言を強制する
VBA はデフォルトでは変数を宣言していなくてもいきなり使用することができる。

つまり、そのままだと変数名をタイポしてもコンパイルは通ってしまい、バグの温床になる。

これは非常に危険なので、変数を宣言せずに使用するとコンパイルエラーが発生するように設定する。

「ツール」→「オプション」を選択し、「編集」タブの「コードの設定」にある「変数の宣言を強制する」にチェックを入れる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c1cda2ad-40ac-4b77-09e4-77be6dffb0f2.jpeg)

これで、新規にモジュールなどを追加すると、先頭に自動的に `Option Explicit` が挿入されるようになり、変数の宣言を強制させることができる。

##デバッガ
###ブレークポイントを設定する
エディターの左端をクリックすると、ブレークポイントを設定できる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8c16203e-6111-1c9a-3c1d-383f0211f491.jpeg)

###デバッグ中の操作

|キー|操作|
|:--|:--|
|F8|ステップイン|
|Shift + F8|ステップオーバー|
|F5|再開|

###変数に代入されている値を確認する
####マウスカーソルをホバーさせる
![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ae49e568-15f6-606b-76ba-5ffde977cbd7.jpeg)

変数の上にマウスカーソルを乗せると、変数に代入されている値が表示される。

####ウォッチウィンドウを使う
「表示」→「ウォッチウィンドウ」でウォッチウィンドウを表示できる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2c8178cb-c1c5-a9e5-4044-0ad2a24efab5.jpeg)

「ウォッチウィンドウ」上で右クリックして、「ウォッチ式の追加」を選択すると、ダイアログが表示されるのでウォッチしたい変数の名前を入力して「OK」をクリックする。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7be0b657-51d1-3e6b-9126-256eccd4bcc3.jpeg)

ウォッチウィンドウで変数の中身を見れるようになる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7dfbd64d-df8a-8ebf-ad0b-00a421871441.jpeg)

####イミディエイトウィンドウを使う
イミディエイトウィンドウで変数の値を出力させることもできる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/5a0c95a4-14d1-91aa-04c3-c9a28b566919.jpeg)

##### `?` で出力する
![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/042e7c16-7f3a-10c2-4abb-f51d236990a7.jpeg)

`?` の後ろに確認したい変数名を記述して `Enter` で、内容を確認できる。

コメントで教えていただきました。ありがとうございます。

##Ctrl + Z, Ctrl + Y の罠
一般的に、 `Ctrl + Z` は「元に戻す」、 `Ctrl + Y` は「やり直し」のショートカットとして使用されていることが多い。

Excel の VB エディタも `Ctrl + Z` は「元に戻る」のショートカットになっている。

しかし、 `Ctrl + Y` は「やり直し」のショートカットではない。
`Ctrl + Y` は<ins>行の削除</ins>のショートカットになっている <del>ﾌｧｯｸ!!</del>。

`Ctrl + Z` で編集を戻してて、「あ、戻しすぎた」と思ってあわてていつものノリで `Ctrl + Y` を連打すると、行がどんどん削除されていくという罠。
狂気を感じるショートカット設定です。

じゃあ、「やり直し」はどのショートカットで入力できるのかというと、

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/86052eb7-3acc-6454-ef00-82a2a48d1cb1.jpeg)

ありません。
どうみても悪質な罠です。本当にありがとうございました。

**追記**

[ショートカットキーを設定する方法](#shortcut_key) を教えていただきました。

## <span id="shortcut_key">ショートカットを設定する</span>
コメントでショートカットを設定する方法を教えていただきました。

- ツールバーを右クリックして、[ユーザー設定]を選択

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/050185bd-d0c1-2a5c-8363-9be28309612f.jpeg)

- 「ユーザー設定」ダイアログが開くので、そのままツールバーのコメントブロックのアイコンをクリックする。

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/988ad2cd-9609-c3a3-8caf-f52b1109e496.jpeg)

- 「選択したボタンの編集」を選択して、「名前」の末尾に `(&C)` と入力する。
- 「イメージとテキストを表示」または「テキストのみ表示」を選択する。

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/bdb4753c-1dc8-898c-e4f7-e70b31605ad8.jpeg)

- 「閉じる」ボタンで「ユーザー設定」ダイアログを閉じる。

これで、 `Alt` + `C` でコメントブロックを実行できるようになる。
`C` 以外のキーに設定したい場合は、 `(&T)` とか `(&K)` といった感じで設定すればいい。

**注意**

ツールバーにボタンが表示されていないと、ショートカットが働かない。
もし、設定したのにショートカットがきかない場合は、ツールバーにボタンが表示されているか確認する。

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/873d8064-b6e3-efa4-4429-f61d871a6978.jpeg)

↑の状態だと、コメントブロックは動作するが、非コメントブロックは見えなくなっているのでショートカットが動作しない。

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/fc37b42a-2236-87d1-c381-69b37782f351.jpeg)

↑のように、ボタンが見えるようにウィンドウサイズやツールバーの位置を調整しないといけない。

**参考**

- [複数行コメントアウトのショートカットキー:VBE(Visual Basic Editor)](http://www.relief.jp/itnote/archives/003813.php)
- [独自のショートカットキーを設定する：Excel 一般｜即効テクニック｜Excel VBAを学ぶならmoug](http://www.moug.net/tech/exopr/0020010.html)

#文法の基礎
##コメント
シングルクォーテーションより後ろはコメント行になる。

```vbnet:
' コメント
```

###複数行コメント

複数行コメント？　なにそれおいしいの？

代わりに、「表示」→「ツールバー」→「編集」を選択して編集ツールバーを表示させる。
すると、選択している範囲を一気にコメントアウトできる「コメント ブロック」とコメントの解除ができる「非コメント ブロック」が使えるようになる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/0dbdd657-84f4-e5f6-2505-1497c6602380.jpeg)

ショートカットは割り当てられていないみたい <del>ﾌｧｯｸ!!</del>。

**追記**

[ショートカットキーを設定する方法](#shortcut_key) を教えていただきました。

##変数の宣言
```vbnet:
Dim a As Integer
```

`Dim <変数名> As <型>` で変数を宣言できる。

##代入
###基本
```vbnet:
Public Sub procedure()
    Dim i As Integer
    i = 10
    
    Debug.Print i
    
    Dim s As String
    s = "string"
    Debug.Print s
    
    Dim b As Boolean
    b = True
    Debug.Print b
    
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
 10 
string
True
```

基本は、 `<変数名> = <代入する値>` で変数に値を代入できる。

###オブジェクト型の代入
代入する値が__オブジェクト型__の場合は注意が必要。

```vbnet:これはエラーになる
Public Sub procedure()
    Dim c As Collection
    
    c = New Collection
    
    Debug.Print c.Count
End Sub
```

これを実行すると、「引数は省略できません」という意味不明なエラーメッセージが表示される <del>ﾌｧｯｸ!!</del>。

正しくは、以下のように変数の前に `Set` を入れる。

```vbnet:
Public Sub procedure()
    Dim c As Collection
    
    Set c = New Collection ' 変数の前に Set を入れる
    
    Debug.Print c.Count
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
 0 
```

オブジェクト型というのは、 `Integer` や `Boolean`、 `String` などの基本的な型以外全てを指す。

これが意外と厄介モノで、つい記述を忘れて実行時にエラーが連発したりする。

####Function の戻り値でも必要
```vbnet:
Public Sub procedure()
    Dim c As Collection
    
    Set c = GetCollection
End Sub

Public Function GetCollection() As Collection
    GetCollection = New Collection
End Function
```

`procedure` プロシージャで `Set c = GetCollection` としているため大丈夫そうに見えるが、これを実行するとエラーが発生する。

正しくは、以下のように実装する。

```vbnet:
Public Sub procedure()
    Dim c As Collection
    
    Set c = GetCollection
End Sub

Public Function GetCollection() As Collection
    Set GetCollection = New Collection
End Function
```

Function の戻り値がオブジェクト型の場合も、 `Set` を付けなければならない。

##If 文
```vbnet
    If Number = 1 Then
        ' ...
    ElseIf Nember = 5 Then
        ' ...
    Else
        ' ...
    End If
```

## ループ
### For 文
```vbnet
Dim i As Integer

For i = 0 To 5
    Debug.Print i
Next
```

```text:実行結果
 0 
 1 
 2 
 3 
 4 
 5 
```

### Do While 文
```vbnet
Dim i As Integer

Do While i < 5
    Debug.Print i
    i = i + 1
Loop
```

```text:実行結果
 0 
 1 
 2 
 3 
 4 
```

##プロシージャ
###プロシージャの種類

プロシージャには、 Sub と Function の２種類が存在する。

```vbnet:
Public Sub SubProcedure()
    Debug.Print "SubProcedure"
    Debug.Print FunctionProcedure
End Sub

Public Function FunctionProcedure() As String
    FunctionProcedure = "FunctionProcedure"
End Function
```

```text:イミディエイトウィンドウ
Sheet1.SubProcedure
SubProcedure
FunctionProcedure
```

Sub は戻り値無し、 Function は戻り値有りという違いがある。

###Function の戻り値
Function の戻り値は、プロシージャ名と同じ名前の変数？に戻り値を代入することで実現できる。

```vbnet:
Public Sub SubProcedure()
    Debug.Print "SubProcedure"
    Debug.Print FunctionProcedure
End Sub

Public Function FunctionProcedure() As String
    FunctionProcedure = "FunctionProcedure"
    Debug.Print "ここも実行される"
End Function
```

```text:イミディエイトウィンドウ
Sheet1.SubProcedure
SubProcedure
ここも実行される
FunctionProcedure
```

Java などの `return` とは異なり、戻り値を設定したあとも処理が続行される。

そのプロシージャの処理を中断させたい場合は `Exit Function` または `Exit Sub` と記述する。

```vbnet:
Public Sub SubProcedure()
    Debug.Print "SubProcedure"
    Debug.Print FunctionProcedure
End Sub

Public Function FunctionProcedure() As String
    FunctionProcedure = "FunctionProcedure"
    Exit Function
    Debug.Print "ここは実行されない"
End Function
```

```text:イミディエイトウィンドウ
Sheet1.SubProcedure
SubProcedure
FunctionProcedure
```

###プロシージャの呼び出し方
```vbnet:
Public Sub OneParameter(a As Integer)
    Debug.Print a
End Sub

Public Sub TwoParameter(a As Integer, b As Integer)
    Debug.Print a + b
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.OneParameter 1
 1 
Sheet1.TwoParameter 1, 2
 3 
```

プロシージャを単独で呼び出す場合、丸括弧は不要（付けるとコンパイルエラーになる）。

ただし、下記例のように、あるプロシージャの引数に別のプロシージャの呼び出しを渡す場合などは、丸括弧が必要になる。

```vbnet:
Public Sub OneParameter(a As Integer)
    Debug.Print TwoParameter(a, 10) ' TwoParameter の呼び出しに括弧が必要になる
End Sub

Public Function TwoParameter(a As Integer, b As Integer) As Integer
    TwoParameter = a + b
End Function
```

```text:イミディエイトウィンドウ
Sheet1.OneParameter 5
 15 
```

変数に代入するときに、右辺でプロシージャを呼ぶ場合も必要になる。

```vbnet:
Public Sub procedure()
    Dim s As String
    s = TypeName("hoge") ' 括弧が必要
    Debug.Print s
End Sub
```

###プロシージャの処理を途中で中断させる
```vbnet:MyClass
Public Sub SubProcedure()
    Debug.Print "[Sub] before"
    Exit Sub
    Debug.Print "[Sub] after"
End Sub

Public Function FunctionProcedure()
    Debug.Print "[Function] before"
    Exit Function
    Debug.Print "[Function] after"
End Function

Public Property Let Value(Value As Integer)
    Debug.Print "[Property] before"
    Exit Property
    Debug.Print "[Property] after"
End Property
```

```vbnet:Module1
Public Sub Execute()
    Dim Instance As New MyClass
    
    Instance.FunctionProcedure
    Instance.SubProcedure
    Instance.Value = 10
    
End Sub
```

```text:実行結果
[Function] before
[Sub] before
[Property] before
```

- プロシージャの処理を途中で中断させたい場合は、 `Exit <プロシージャの種類>` を使用する。
- 「プロシージャの種類」には、 `Sub`, `Function`, `Property` などを指定する。

###省略可能な引数を定義する
```vbnet:
Public Sub Execute()
    SubProcedure "Explicit Value"
    SubProcedure
End Sub

Public Sub SubProcedure(Optional Value As String = "Default Value")
    Debug.Print Value
End Sub
```

`Execute` プロシージャを実行する。

```text:実行結果
Explicit Value
Default Value
```

- `Optional <引数の宣言> = <デフォルト値>` とすることで、省略可能な引数を定義できる。


##データ型を確認する
```vbnet
Dim a As String
Dim b As Boolean
Dim c As New Collection

Debug.Print "a : " & TypeName(a)
Debug.Print "b : " & TypeName(b)
Debug.Print "c : " & TypeName(c)
```

```text:実行結果
a : String
b : Boolean
c : Collection
```

- `TypeName()` でデータ型を文字列で取得できる。

##文字列中に改行コードを入れる
```vbnet
Dim Text As String
Text = "aaa" & vbCrLf & "bbb"

Debug.Print Text
```

```text:実行結果
aaa
bbb
```

- `vbCrLf` という定数が定義されているので、それを利用する。
- `vbCrLf` は、キャリッジリターンとラインフィード。

##コードの途中で改行を入れる
```vbnet
Dim Text _
As String

Text = _
"Hoge" & _
"Fuga"

Debug.Print Text
```

```text:実行結果
HogeFuga
```

- `_` を末尾に記述することで、コードを途中で改行させられる。

# コレクション（List）を使用する
VBA には配列もあるが、サイズの調整とかを自分でしないといけないのでなんかアレ。

## 基本
```vbnet
Dim list As New Collection

list.Add "hoge"
list.Add "fuga"

Debug.Print list(1)
Debug.Print list.Item(2)
```

```text:実行結果
hoge
fuga
```

- `Collection` というクラスを `New` することで、コレクション（`List` みたいなの）を生成できる。
- `Add()` メソッドで要素を追加できる。
- 丸括弧か `Item()` メソッドを使うことで、インデックス指定で要素を取得できる。
- インデックスは、まさかの `1` 始まり。

## ループ
```vbnet
Dim list As New Collection

list.Add "hoge"
list.Add "fuga"
list.Add "piyo"

Dim i As Integer

For i = 1 To list.Count
    Debug.Print (i & " : " & list(i))
Next

Dim e As Variant

For Each e In list
    Debug.Print e
Next
```

```text:実行結果
1 : hoge
2 : fuga
3 : piyo
hoge
fuga
piyo
```

- `Count` で要素数を取得できるので、それで `For` ループを回すことができる。
- もしくは、 `For Each` でループすることもできる。

## 要素の削除
```vbnet
Dim list As New Collection

list.Add "hoge"
list.Add "fuga"
list.Add "piyo"

list.Remove 2

Dim i As Integer

For i = 1 To list.Count
    Debug.Print (i & " : " & list(i))
Next
```

```text:実行結果
Hoge
1 : hoge
2 : piyo
```

- `Remove()` メソッドを使うと、インデックスを指定して要素を削除できる。

#連想配列を使用する
##基本
```vbnet
Dim Map As Object
Set Map = CreateObject("Scripting.Dictionary")

Map("hoge") = "Hoge"

Debug.Print "hoge: " & Map("hoge")
```

```text:実行結果
hoge: Hoge
```

- VBA の連想配列は、 `Dictionary` を使う。
- 標準だと、この型をそのまま使うことはできず、 `CreateObject("Scripting.Dictionary")` でインスタンスを生成する。

##Dictionary の型を使用できるようにする
- [ツール] → [参照設定] を開く。
- `Microsoft Scripting Runtime` のチェックを入れる。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/48247597-90af-b48c-a4b5-fb06c67dbe3b.jpeg)

こうすると、 `Dictionary` の型が利用できるようになる。

```vbnet
Dim Map As New Dictionary

Map("hoge") = "Hoge"

Debug.Print "hoge: " & Map("hoge")
```

##ループ処理
```vbnet
Dim Map As New Dictionary

Map("hoge") = "Hoge"
Map.Add "fuga", "Fuga"

Dim Key As Variant

For Each Key In Map
    Debug.Print "Key=" & Key & ", Value=" & Map(Key)
Next
```

```text:実行結果
Key=hoge, Value=Hoge
Key=fuga, Value=Fuga
```

##Dictionary に定義されているメソッド

| フィールド名 |   引数   |                   説明                   |
|--------------|----------|------------------------------------------|
| `Exists`     | キー     | 指定したキーの要素が存在するか確認する。 |
| `Count`      | -        | 保有している要素の数を取得する。         |
| `Keys`       | -        | 保有しているキーを配列で取得する。       |
| `Add`        | キー, 値 | 要素を追加する。                         |
| `Item`       | キー     | 指定したキーの値を取得する。             |
| `Items`      | -        | 全ての要素の値を配列で取得する。         |

##セルで使用できる関数を VBA から利用する
```vbnet
Dim Summary As Integer
Summary = WorksheetFunction.Sum(Range("A1:C1"))
Debug.Print Summary
```

- `WorksheetFunction` にメソッドとして関数が定義されている。
- 範囲やセルを渡していた部分には、 `Range` を渡すようにする。


#フォルダ・ファイル操作
##ディレクトリ・ファイルが存在するか確認する
```vbnet
Dim ExistsDirPath As String
Dim NotExistsDirPath As String

ExistsDirPath = "D:\tmp\excel"
NotExistsDirPath = "D:\xxxx"

Debug.Print "> " & Dir(ExistsDirPath, vbDirectory)
Debug.Print "> " & Dir(NotExistsDirPath, vbDirectory)
Debug.Print "> " & (Dir(NotExistsDirPath, vbDirectory) = "")
```

```text
> excel
> 
> True
```

- `Dir()` 関数で存在チェックが行える。
- ディレクトリをチェックする場合は、第二引数に `vbDirectory` を渡す。
- 存在する場合は、どのディレクトリの名前が、存在しない場合は空文字が返される。
- ファイルの場合は、 `vbDirectory` 無しで `Dir()` 関数を使えばいい。

##ディレクトリを作成する
###1階層だけ
```vbnet
MkDir "D:\path\to\dir"
```

###複数階層をまとめて生成
```vbnet
Private Declare Function SHCreateDirectoryEx Lib "shell32" Alias "SHCreateDirectoryExA" ( _
                                                                  ByVal hwnd As Long, _
                                                                  ByVal pszPath As String, _
                                                                  ByVal psa As Long) As Long

Public Function MkDirs(Path As String) As Long
    MkDirs = SHCreateDirectoryEx(0&, Path, 0&)
End Function

Public Sub Test()
    Debug.Print MkDirs("D:\tmp\hoge\fuga")
End Sub
```

- Win API を呼び出しているらしい。
- 作成に成功したら、`0`が返される。
- 既にフォルダが存在する場合、自分の環境では `183` が返された。

##カレントフォルダを取得する
```vbnet
Dim CurrentPath As String
CurrentPath = ActiveWorkbook.Path
```

- `ActiveWorkbook.Path` で現在開いているブックのあるディレクトリのパスを取得できる。

##ファイル出力
###基本
```vbnet
Open "D:\tmp\test.txt" For Output As #1
Print #1, "Hello World!!"
Close #1
```

```text:test.txt
Hello World!!

```

- `Open <ファイルパス> For <処理> As <ファイルNO>` で、ファイルを開く。
- `Print <ファイルNO>, "出力内容"` で、ファイルに文字列を書き込む（末尾に改行が自動で付く）。
- `Close <ファイルNO>` で、ファイルを閉じる。
- `<処理>` には、以下が指定可能。

| 処理種別 |        説明        |
|----------|--------------------|
| `Input`  | 読み取り           |
| `Output` | 書き込み（上書き） |
| `Append` | 書き込み（追記）   |

- `<ファイルNO>` は、操作しているファイルを識別するための数値。
- 複数のファイルを同時に制御する必要がないなら、 `#1` とかで OK。
- 複数ファイルを同時に扱う場合は、どの数値を利用しているか管理が大変になるので、その場合は以下のようにする。

```vbnet
Dim FileNo As Integer
FileNo = FreeFile ' ★FreeFile 関数を使う

Open "D:\tmp\test.txt" For Output As FileNo
Print #FileNo, "Hello World!!"
Close FileNo
```

- `FreeFile` は、現在利用可能なファイルNO を返す関数。

###Print ステートメントで改行コードを付けないようにする
```vbnet
Open "D:\tmp\test.txt" For Output As #1
Print #1, "Hello World!!";
Close #1
```

```text:test.txt
Hello World!!
```

- `Print` ステートメントの末尾にセミコロン `;` をつけると、末尾の改行が付かなくなる。

###エラー時のクローズは必要か？
```vbnet
Open "D:\tmp\test.txt" For Output As #1
Print #1, "Hello World!!"
```

こんな感じで、 `Close` することなくプロシージャが正常すると、ファイルは開きっぱなしになる。
このファイルのロックは、 Excel のブックを閉じるまで解放されない。

```vbnet
Open "D:\tmp\test.txt" For Output As #1
Print #1, "Hello World!!"
Err.Raise 10000, , "test"
```

一方、上記のように `Close` 前にエラーで落ちた場合、エラーダイアログで「終了」を選択すると、ファイルのロックは解放された。

つまり、 Java でいうところの「`finally` ブロックで必ずリソースが解放されるように実装する」みたいな制御は必要ないのかもしれない。

**※あくまで個人的に軽く試した結果なので、落とし穴の可能性もあるため過信しすぎないように。**

#メッセージボックス
##基本
```vbnet
MsgBox "Hello World!!"
```

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d92c8c13-3f8d-5ed9-55a3-e5dbfe3a7e08.jpeg)

- `MsgBox` 関数を使用することで、メッセージボックスを表示できる。

##表示ボタンの変更
```vbnet
MsgBox "Hello World!!", vbOKCancel
```

|          定数         |       ボタン       |
|-----------------------|--------------------|
| `vbOkOnly`            | OK のみ            |
| `vbOKCancel`          | OK, キャンセル     |
| `vbAbortRetryIgnore ` | 中止、再試行、無視 |
| `vbYesNo`             | はい、いいえ       |
| `vbRetryCancel`       | 再試行、キャンセル |

**`vbOKCancel` を指定した場合**

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a9f82dde-2c9a-9099-6db9-6dd390f8d9fe.jpeg)

- `MsgBox` の第二引数でボタンの表示を切り替えができる。

###デフォルトで選択されているボタンを変更する
```vbnet
MsgBox "Hello World!!", vbOKCancel + vbDefaultButton2
```

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a4f46298-0701-8c3b-340b-dd1f3251da99.jpeg)

- 第二引数に `vbDefaultButtonN` を加算することで指定する。
- `N` のところは 1～4 がある（左から１→２→３→４）。

##アイコンを変更する
```vbnet
MsgBox "Hello World!!", vbCritical
```

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7b2ebe3e-d012-450d-dd31-0d7e53bdc213.jpeg)

|       定数      |       アイコン       |
|-----------------|----------------------|
| `vbCritical`    | 警告アイコンを表示   |
| `vbQuestion`    | 問合せアイコンを表示 |
| `vbExclamation` | 注意アイコンを表示   |
| `vbInformation` | 情報アイコンを表示   |

- 第二引数でアイコンを指定できる。

##ボタンの変更・アイコンの変更を組み合わせる
```vbnet
MsgBox "Hello World!!", vbOKCancel + vbDefaultButton2 + vbCritical
```

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/b9ccffdc-6698-4892-138e-0e5091557ed1.jpeg)

- 組み合わせたいオプションを `+` で加算する。

##押下されたボタンの情報を取得する
```vbnet
Dim Clicked As Integer
Clicked = MsgBox("Hello World!!", vbOKCancel)

Debug.Print Clicked
```

```text:キャンセルボタンを押した場合
2
```

| 値 | 対応する定数 | 押されたボタン |
|----|--------------|----------------|
|  1 | `vbOK`       | OK             |
|  2 | `vbCancel`   | キャンセル     |
|  3 | `vbAbort`    | 中止           |
|  4 | `vbRetry`    | 再試行         |
|  5 | `vbIgnore`   | 無視           |
|  6 | `vbYes`      | はい           |
|  7 | `vbNo`       | いいえ         |

#クラスを定義する
##クラスモジュールを追加する
「プロジェクトウィンドウ」で対象のマクロを右クリックし、「挿入」→「クラスモジュール」を選択する。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4e2c87c0-1897-2121-5c9e-47b8fea55eb5.jpeg)

クラスはデフォルトでは `Class1` のような名前になる。
名前を変更する場合は、「プロパティウィンドウ」を表示して、「オブジェクト名」で指定する。

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2163a0ad-46d7-5227-d44d-b7a2909e0205.jpeg)

##インスタンスを生成する
```vbnet:
Public Sub procedure()
    Dim mine As New MyClass
    Set mine = New MyClass
End Sub
```

インスタンスを生成する方法は、以下のいずれかがある。

- `Dim` で宣言するときに、 `As New <クラスモジュール名>` とする。
- 変数に代入するときに `New <クラスモジュール名>` とする。

##コンストラクタ
```vbnet:MyClass
Option Explicit

Private Sub Class_Initialize()
    Debug.Print "initialize"
End Sub

Private Sub Class_Terminate()
    Debug.Print "terminate"
End Sub
```

```vbnet:
Public Sub procedure()
    Dim mine As MyClass
    Set mine = New MyClass
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
initialize
terminate
```

`Class_Initialize`、`Class_Terminate` という名前のプロシージャを宣言することで、インスタンス生成時と削除時に処理を挟むことができる。

###引数が宣言できない！
ただし、このコンストラクタには引数を宣言できない <del>ﾌｧｯｸ!!</del>。

妥協策としては、 

- `init` のようなプロシージャを作り、コーディング規約で `init` というプロシージャがあれば必ずインスタンス生成後に実行させること、みたいな縛りを入れる。
- 標準モジュールにファクトリ用のプロシージャを定義する。

[こんな方法](http://foolexp.wordpress.com/2012/02/21/%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%81%A7%E5%BC%95%E6%95%B0%E3%82%92%E5%85%A5%E3%82%8C%E3%81%9F%E3%81%84/)もある。

###Dim で宣言するときに New しているだけでは実行されない！
```vbnet:MyClass
Option Explicit

Private Sub Class_Initialize()
    Debug.Print "initialize"
End Sub

Private Sub Class_Terminate()
    Debug.Print "terminate"
End Sub

Public Sub Method()
    Debug.Print "Method"
End Sub
```

```vbnet:
Public Sub procedure()
    Dim mine As New MyClass
    Debug.Print "before call Method"
    mine.Method
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
before call Method
initialize
Method
terminate
```

`Dim` の宣言と同時に `New` した場合、その時点では `Class_Initialize` は呼ばれない。
`Class_Initialize` が実行されるのは、最初にそのインスタンスが使用されるときなので注意。

##静的メンバーは定義できない！
VBA のクラスモジュールには、静的な変数やプロシージャを定義できない <del>ﾌｧｯｸ!!</del>。

静的なプロシージャなどは、標準モジュールに定義しなければならない。

##自分のメンバーにアクセスする
```vbnet:MyClass
Option Explicit

Public Name As String

Public Sub Introduce()
    Debug.Print "My name is " & Me.Name & "."
End Sub
```

```vbnet:
Public Sub procedure()
    Dim mine As New MyClass
    mine.Name = "Taro"
    mine.Introduce
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
My name is Taro.
```

`Me.<メンバー>` で、自分のメンバーにアクセスできる。

ただし、この方法でアクセスできるメンバーは、可視性が `Public` で宣言されている必要がある。

`Private` なメンバーにアクセスする場合は、 `Me` は使えない。

##プロパティを定義する
```vbnet:
Option Explicit

Private My_Age As Integer

Public Property Let Age(In_Age As Integer)
    My_Age = In_Age
End Property

Public Property Get Age() As Integer
    Age = My_Age
End Property
```

```vbnet:
Public Sub procedure()
    Dim mine As New MyClass
    mine.Age = 17
    
    Debug.Print mine.Age
    
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
 17 
```

`Public Property Let <プロパティ名>(引数)` で書き込み用のプロパティを、
`Public Property Get <プロパティ名>() As <型>` で読み取り用のプロパティを宣言できる。

プロパティは、 `<変数名>.<プロパティ名>` でアクセスできるようになる。

##With でメソッドの呼び出しをまとめる
```vbnet:MyClass
Private MyValue As Integer

Public Sub SubProcedure(Message As String)
    Debug.Print "Sub Procedure : " & Message
End Sub

Public Property Let Value(Value As Integer)
    MyValue = Value
End Property
```

```vbnet:Module1
Public Sub Execute()
    Dim Instance As New MyClass
    
    With Instance
        .SubProcedure "with statement"
        .Value = 100
    End With
End Sub
```

```text:実行結果
Sub Procedure : with statement
```

- `With` を使うことで、あるインスタンスのメソッド（プロパティ）呼び出しをまとめることができる。

##インターフェースを実装する
```vbnet:MyInterface
Public Sub Method()
End Sub
```

```vbnet:Hoge
Implements MyInterface

Public Sub MyInterface_Method()
    Debug.Print "Hoge Method"
End Sub
```

```vbnet:Fuga
Implements MyInterface

Public Sub MyInterface_Method()
    Debug.Print "Fuga Method"
End Sub
```

```vbnet:Module1
Public Sub Execute()
    Dim HogeInstance As MyInterface
    Dim FugaInstance As MyInterface
    
    Set HogeInstance = New Hoge
    Set FugaInstance = New Fuga
    
    HogeInstance.Method
    FugaInstance.Method

End Sub
```

`Module1.Execute` プロシージャを実行する。

```text:イミディエイトウィンドウ出力
Hoge Method
Fuga Method
```

- VBA では、インターフェースを定義したポリモーフィズムを利用できる。
- インターフェースの定義は、クラスモジュールで行う（`MyInterface`）。
    - インターフェースには、実装が空っぽのプロシージャ定義だけを記載する。
- インターフェースを実装するときは、まずクラスモジュールの先頭で `Implements <実装するインターフェース>` と記述する。
    - 次に、インターフェースで定義されたメソッドの具体的な実装を記述する。
    - この時、実装するメソッドの名前にはプレフィックスとして `<インターフェースの名前>_` を追加する（`MyInterface_Method`）。
- 使用できるのはインターフェースなので、 Java でいう抽象クラスみたいなのは作れない ~~ﾌｧｯｸ!!~~ 。

##ダウンキャスト
上述のインターフェース実装で作成した `Hoge` と `Fuga` のインスタンスは、 `MyInterface` 型の変数として定義されている。
これをダウンキャストして、 `Hoge` 型の変数に代入するには以下のようにする。

```vbnet:
Public Sub Execute()
    Dim Instance As MyInterface
    Set Instance = New Hoge
    
    Dim HogeInstance As Hoge
    Set HogeInstance = Instance ' ダウンキャスト
    
    HogeInstance.MyInterface_Method
End Sub
```

```text:実行結果
Hoge Method
```

普通にダウンキャスト後の型の変数に代入すればいい。


#例外
##例外をスローする
```vbnet:
Public Sub procedure()
    Err.Raise 10000
End Sub
```

![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c51e8411-b93b-3b29-4092-38ef188471a7.jpeg)

`Err.Raise` で例外をスローできる。

第一引数のエラーコードが必須。第三引数にエラーの説明を載せることができる。

エラーコードは、 0 ～ 512 が予約済みで、ユーザーが定義できるのは 513 ～ 65535。

##例外をキャッチする
```vbnet:
Public Sub procedure()
    On Error GoTo EXCEPTION

    Err.Raise 10000, , "My Error"
    
    Exit Sub ' これを忘れないように注意！
EXCEPTION:
    Debug.Print Err.Number & " : " & Err.Description
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
10000 : My Error
```

`On Error GoTo <ラベル名>` を先頭に書き、ラベル名で指定したラベルを宣言する。

すると、例外がスローされたときに指定したラベルに処理が移る。

その際、 `Err` オブジェクトにスローされた例外の情報が格納される。

注意しないといけないのは、ラベルの前にプロシージャを終了させる `Exit Sub` または `Exit Procedure` を入れないといけない、という点。

あくまでラベルなので、これが漏れていると普通に例外処理を書いた領域が実行されてしまう <del>ﾌｧｯｸ!!</del>。

##例外の情報が・・・消えた・・・？
```vbnet:
Public Sub procedure()
    On Error GoTo EXCEPTION

    Err.Raise 10000, , "My Error"

    Exit Sub
EXCEPTION:
    hoge
    Debug.Print Err.Number & " : " & Err.Description
End Sub

Public Sub hoge()
    On Error Resume Next
    Debug.Print "hoge"
End Sub
```

```text:イミディエイトウィンドウ
Sheet1.procedure
hoge
0 : 
```

例外が発生したあとで別のプロシージャ（`hoge`）を呼び出し、その後で `Err` の情報を出力しようとすると、エラーコードと説明が 0 と空になるという現象が発生している。

原因は、 `hoge` プロシージャで使用されている `Exit Sub` ステートメントにある。

`Err` の仕様として、 `On Error` ステートメントが実行されると、 `Err` に設定されている情報がリセットされるようになっている <del>ﾌｧｯｸ!!</del>。

#マクロの実行を終了させる
```vbnet:
Public Sub procedure()
    End
End Sub
```

`End` でマクロの実行を終了させることができる。

#他のブックをこっそり開いて操作する
```text:ファイル構成
|-main.xlsm
`-sub.xlsx
```

**sub.xlsx**

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2cd745e9-601c-534b-5449-44b81e45c872.jpeg)

```vbnet:main.xlsmのModule1
Public Sub Execute()
    Dim BookPath As String
    
    BookPath = "他のブックへのフルパス"
    
    If Dir(BookPath) = "" Then
        MsgBox "ファイルが見つかりません : " & BookPath
        Exit Sub
    End If
    
    Dim SubBook As Workbook
    
    Application.ScreenUpdating = False
    
    Set SubBook = Workbooks.Open(BookPath)
    ThisWorkbook.Activate
    
    Application.ScreenUpdating = True
    
    Dim Value As String
    Value = SubBook.Worksheets("Sheet1").Range("A1").Value
    
    Debug.Print Value
End Sub
```

`Module1.Execute` プロシージャを実行する。

```text:実行結果
sub book
```

- `main.xlsm` のマクロから、 `sub.xlsx` をこっそり開いてセルの値を参照している。
- `Dir(＜フルパス＞)` の戻り値が空文字かどうかでファイルが存在するかどうかをチェックする。
- `Workbooks.Open` でブックを開く。
    - `Application.ScreenUpdating = False` と `ThisWorkbook.Activate` でフォーカルが移らないようにしている。
- `Workbooks.Open` の戻り値が、開いたブックを表す `Workbook` オブジェクトになっているので、そのメソッドを使ってシートの情報にアクセスできる。

#MVC で書く
Excel VBA を書いていると、シートの値の参照やセルの変更などで、シートに関する処理が肥大化しやすい。

その中にドメインロジックが紛れ込むと死にたくなるので、 MVC で書いて View と Model を分離するようにしたい。

以下が処理の流れのイメージと、実装の例。
（個人的に考えた方法なので、一般的な VBA 実装はどうなるのかは不明）

##処理の流れのイメージ（シーケンス図）
![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/9714133c-b24d-526f-1189-bc1c1ebafb66.jpeg)

1. ユーザーアクションがあったら、シートや標準モジュールに宣言しているプロシージャを実行するように紐付けておく。
2. 標準モジュールで、必要な MVC のインスタンスを生成し、コントローラに処理の開始を指示する。
3. コントローラは、ビューから必要な値を取得する。
4. コントローラは、ビューから取得した値をモデルに渡す。
5. モデルは、受け取った値を元に処理を実行する。
6. コントローラは、モデルの処理が完了したら、ビューに結果の表示を指示する。
7. ビューは、モデルの値を参照して結果を画面（シート）に反映する。

自分が実装したときは、１つのシートにつき１つのビューを作るようにしていた。
ビューが複雑な場合は、カタマリごとにビュークラスを設けてもいいかもしれない。

とにかく、ビューは他（コントローラやモデル）から隔離し、独立したクラスを定義すべきだと考えている。

###ビューを独立したクラスに分ける理由
####ビュー操作はノイズになる
ビューの実装は、主に Excel の API を使ったものになる。

Excel の API の使い方などは、モデル（ドメインロジック）にとっては、全く無関係なノイズでしかない。

なので、可読性の面からビューの操作は独立したクラスに隔離すべきだと考える。

####ビューの変更に強くする
Excel の見た目（セルの位置等）は頻繁に変更されることが予想される。

ビューの操作をクラスにまとめておけば、ビューの変更に対して変更箇所を簡単に特定できるようになる。
少なくとも１シート１ビューとしておけば、変更されたシートに対応するビュークラスが修正対象であると、容易に判断できる。

また、ビュークラス内の実装では、ある項目へのアクセス（値の設定・取得）を、特定のプロシージャを介してのみ行うようにする。
こうすることで、変更箇所をより局所化できる。

####ビュー操作を抽象化でき、可読性・再利用性が向上する
ビューの処理は、「"A1" のセルの値を取得する」のように、非常に抽象度の低い実装になる。

```vbnet:抽象度が低い実装
Dim Name As String
Name = ActiveSheet.Range("A1").Value
```

実は、シートが何らかの申請書で、 "A1" には申請者の名前が入力されていたとする。

ビュークラスを使って操作をカプセル化すれば、 

```vbnet:抽象度が高い実装
Dim ApplicationSheet As New ApplicationSheet
Dim Name As String

Name = ApplicationSheet.ApplicantName
```

というふうに実装の抽象度が上がり、可読性があがる。

また、名前を取得する操作が共通化されるので、他のところでも名前が必要になった場合、簡単に使いまわすことができる。

Excel の API の操作方法を知ることなく欲しい情報にアクセスできるようになるので、実装しやすさも向上する。

##MVC の実装例
###前置き
あくまで例のための実装なので、「`SUM` 関数や `AVERAGE` 関数つかえばマクロ組む必要ねーじゃん」というツッコミはなしで。

###画面
![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ac805028-e402-18a1-3d20-55c915b7bfff.jpeg)

A列に点数を列挙して、「計算」ボタンをクリックすると合計と平均が表示される、というマクロを書く。

###実装
```vbnet:標準モジュール
Option Explicit

Public Sub CalculateSummary()
    Dim Model As New Model
    Dim View As New View
    Dim Control As New Control
    
    With Control
        .Model = Model
        .View = View
    End With
    
    Control.CalculateSummary
    
End Sub
```

```vbnet:Control
Option Explicit

Private My_Model As Model
Private My_View As View

Public Property Let Model(Model As Model)
    Set My_Model = Model
End Property

Public Property Let View(View As View)
    Set My_View = View
End Property

Public Sub CalculateSummary()
    Dim Scores As Collection
    
    Set Scores = My_View.GetScores
    
    My_Model.CalculateSummary Scores
    
    My_View.ShowResult My_Model
End Sub
```

```vbnet:View
Option Explicit

Public Function GetScores() As Collection
    Dim Scores As New Collection
    Dim ScoreCell As Range
    
    Set ScoreCell = ActiveSheet.Range("A2")
    
    Do While ScoreCell.Value <> ""
        Scores.Add CInt(ScoreCell.Value)
        Set ScoreCell = ScoreCell.Offset(1, 0)
    Loop
    
    Set GetScores = Scores
End Function

Public Sub ShowResult(Model As Model)
    Summary = Model.Sum
    Average = Model.Average
End Sub

Private Property Let Summary(Sum As Integer)
    ActiveSheet.Range("C2").Value = Sum
End Property

Private Property Let Average(Ave As Double)
    ActiveSheet.Range("D2").Value = Ave
End Property
```

```vbnet:Model
Option Explicit

Private My_Sum As Integer
Private My_Average As Double

Private Sub Class_Initialize()
    My_Sum = 0
    My_Average = 0
End Sub

Public Sub CalculateSummary(Scores As Collection)
    Dim Score As Variant
    
    For Each Score In Scores
        My_Sum = My_Sum + Score
    Next
    
    My_Average = My_Sum / Scores.Count
End Sub

Public Property Get Sum() As Integer
    Sum = My_Sum
End Property

Public Property Get Average() As Double
    Average = My_Average
End Property
```

## Model 用のシートを設ける
コメントで Model 用のシートを設けて、 View シートはそこを参照するようにしたら良いという情報を頂いたので、試してみました。

あと、はてブのコメントで View クラスはシートごとに用意されるコードに書いてはどうかという意見も頂いてその通りな気がしたので、それも試してみました。

> ExcelVBAってモジュールやクラス以外にワークシートやワークブック単位にコードビハインドみたいのが書けるから、ビューはそこに定義したらどうかな。

http://b.hatena.ne.jp/entry/205460573/comment/regicat

### シート

**Sheet**

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/41fdf40f-af29-8a8a-9987-be4f118a3920.jpeg)

- 計算結果を表示する部分のセルを、 `model` シートを参照するように設定する。

**model**

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/dfb0c77b-aa08-bac5-69c9-1f43e6b1b912.jpeg)

- 入力は、逆に `model` シートが View のシートを参照している。

### 実装
![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a70b2e5d-5151-2bf4-63a7-736d5239109d.jpeg)

```vbnet:Main
Option Explicit

Public Sub Execute()
    Dim Model As New Model
    Dim Control As New Control

    With Control
        .Model = Model
        .ModelSheet = ModelSheet
    End With

    Control.CalculateSummary
End Sub
```

```vbnet:Control
Option Explicit

Private My_Model As Model
Private My_ModelSheet As ModelSheet

Public Property Let Model(Model As Model)
    Set My_Model = Model
End Property

Public Property Let ModelSheet(ModelSheet As ModelSheet)
    Set My_ModelSheet = ModelSheet
End Property

Public Sub CalculateSummary()
    Dim Scores As Collection
    
    Set Scores = My_ModelSheet.GetScores
    
    My_Model.CalculateSummary Scores
    
    My_ModelSheet.ShowResult My_Model
End Sub
```

```vbnet:ModelSheet
Option Explicit

Public Function GetScores() As Collection
    Dim Scores As New Collection
    Dim ScoreCell As Range
    
    Set ScoreCell = Range("B3")
    
    Do While ScoreCell.Value <> ""
        Scores.Add CInt(ScoreCell.Value)
        Set ScoreCell = ScoreCell.Offset(1, 0)
    Loop
    
    Set GetScores = Scores
End Function

Public Sub ShowResult(Model As Model)
    Summary = Model.Sum
    Average = Model.Average
End Sub

Private Property Let Summary(Sum As Integer)
    Range("B1").Value = Sum
End Property

Private Property Let Average(Ave As Double)
    Range("B2").Value = Ave
End Property
```

Model は、実装は変更なしだが、 `Instancing` を `2 - PublicNotCreatable` にする。

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/32f115c9-9e6f-d341-7f4e-0551c4d71e06.jpeg)

これは、モジュールの可視性を表している。
デフォルトの `1 - Private` だと `Model` を `ModelSheet` から参照できないので、 `2 - PublicNotCreatable` にしている。

**参考**

- [Excel VBAでのクラスモジュール、標準モジュールのスコープまとめ(Friend, PublicNotCreatableなど) - Qiita](http://qiita.com/sawadybomb/items/12fb921833f51f010e82)

### 説明
- Model 用のシートを追加して、 View シートはそちらの値を参照するようにセルの値を設定する。
- こうすると、 View のシート上でセルの位置を変更しても、 VBA の実装には手を加えなくて良くなる。
- 例では Model 用シートを表示させているが、実際は非表示にすると思う。
- ただ、この方法は入出力するセルの個数が固定な場合は有効だが、動的に変化したりする場合は難しくなることが予想される。
    - その場合は、直接シートを触るモジュールを用意することになると思う。


#ある範囲のセルを１つずつ処理していく実装が書きたい
##画面イメージ
![vba.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/5f563fbb-9699-c610-adfc-2be960614ef5.jpeg)

ボタンをクリックすると、 A 列に入力されている各数値について偶数か奇数かを判定し、結果を B 列に出力する。

A 列を順番に処理していく実装が必要になるが、前述の MVC 構成を維持したまま、これを実装してみる。

##実装
```vbnet:標準モジュール
Option Explicit

Public Sub Execute()
    Dim Model As New Model
    Dim View As New View
    Dim Control As New Control
    
    With Control
        .Model = Model
        .View = View
    End With
    
    Control.Execute
    
End Sub
```

```vbnet:Control
Option Explicit

Private My_Model As Model
Private My_View As View

Public Property Let Model(Model As Model)
    Set My_Model = Model
End Property

Public Property Let View(View As View)
    Set My_View = View
End Property

Public Sub Execute()
    My_View.ForEach Me, "ProcessForEach"
End Sub

Public Sub ProcessForEach(RowIndex As Integer, Number As Integer)
    My_View.SetResult RowIndex, My_Model.IsEvenNumber(Number)
End Sub
```

```vbnet:View
Option Explicit

Public Sub ForEach(Instance As Control, MethodName As String)
    Dim Cell As Range
    Dim RowIndex As Integer
    
    Set Cell = ActiveSheet.Range("A1")
    RowIndex = 1
    
    Do While Cell.Value <> ""
        CallByName Instance, MethodName, VbMethod, RowIndex, CInt(Cell.Value)
        Set Cell = Cell.Offset(1, 0)
        RowIndex = RowIndex + 1
    Loop
    
End Sub

Public Sub SetResult(RowIndex As Integer, IsEvenNumber As Boolean)
    Dim Cell As Range
    
    Set Cell = ActiveSheet.Range("B" & RowIndex)
    
    Cell.Value = IIf(IsEvenNumber, "偶数", "奇数")
End Sub
```

```vbnet:Model
Option Explicit

Public Function IsEvenNumber(Number As Integer) As Boolean
    IsEvenNumber = ((Number Mod 2) = 0)
End Function
```

`CallByName` でリフレクションみたいな感じでプロシージャを実行できることを利用している。

これで、 MVC の構成を維持し、ビューの感心事を外に漏らすことなくセルごとの順次処理が実装できる。

###まとめて処理する
順次処理していかなくても、まとめて処理すれば `CallByName` とか使わずに済むと、何故か後になって気づいたので実装してみる。

```vbnet:標準モジュール
Option Explicit

Public Sub Execute()
    Dim Model As New Model
    Dim View As New View
    Dim Control As New Control

    With Control
        .Model = Model
        .View = View
    End With

    Control.Execute

End Sub
```

```vbnet:Control
Option Explicit

Private My_Model As Model
Private My_View As View

Public Property Let Model(Model As Model)
    Set My_Model = Model
End Property

Public Property Let View(View As View)
    Set My_View = View
End Property

Public Sub Execute()
    Dim Numbers As Collection
    
    Set Numbers = My_View.GetNumbers
    
    Dim Result As Collection
    
    Set Result = My_Model.JudgeEvenNumbers(Numbers)
    
    My_View.SetResults Result
End Sub
```

```vbnet:View
Option Explicit

Public Function GetNumbers() As Collection
    Dim Numbers As New Collection
    Dim Cell As Range

    Set Cell = ActiveSheet.Range("A1")

    Do While Cell.Value <> ""
        Numbers.Add CInt(Cell.Value)
        Set Cell = Cell.Offset(1, 0)
    Loop
    
    Set GetNumbers = Numbers
End Function

Public Sub SetResults(Result As Collection)
    Dim Numbers As New Collection
    Dim Cell As Range
    Dim i As Integer
    
    For i = 1 To Result.Count
        Set Cell = ActiveSheet.Range("B" & i)
        Cell.Value = IIf(Result.Item(i), "偶数", "奇数")
    Next
End Sub
```

```vbnet:Model
Option Explicit

Public Function JudgeEvenNumbers(Numbers As Collection) As Collection
    Dim Result As New Collection
    Dim Number As Variant
    
    For Each Number In Numbers
        Result.Add IIf((Number Mod 2) = 0, True, False)
    Next
    
    Set JudgeEvenNumbers = Result
End Function
```

個人的には、 `CallByName` を使った方が好みかなぁ。
理由は、各プロシージャの役割が小さいから。

後者は、それぞれのプロシージャでループを回す必要があり、仕事量が多くノイズも多く感じる。

#Excel で RESTful な Web API を叩きたい

[Excel-REST](https://github.com/timhall/Excel-REST) を使う。

JSON の変換とかもやってくれるナイスガイ。
MIT ライセンスです。

#セルの背景色を設定する
```vbnet:
Public Sub Execute()
    Range("A1").Interior.ColorIndex = 38
End Sub
```

![excel.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/433f11c0-42bd-55bc-b4f5-e469e1586982.jpeg)

- `Range` の `Interior.ColorIndex` に色ごとの整数値を設定すると、背景色を変更できる。
- 使用できる色と数値の対応は、 [こちら](http://www.happy2-island.com/excelsmile/smile03/capter00602.shtml)に分かりやすくまとめられている。

#参考
- [Office TANAKA - 今さら聞けないVBA「Option Explicitって何？」](http://officetanaka.net/excel/vba/beginner/11.htm)
- [VB エラー操作編](http://www.red.oit-net.jp/tatsuya/vb/Err.htm)
- [プロパティを作成する - EXCEL-LENCE web](http://www.excellenceweb.net/vba/class/class_property.html)
- [複数行をまとめてコメントアウト:Office(オフィス)の使い方-VBE(Visual Basic Editor)](http://www.relief.jp/itnote/archives/001067.php)
- [Office TANAKA - Excel VBAファイルの操作[ブックを開く]](http://officetanaka.net/excel/vba/file/file01.htm)
- [Office TANAKA - Excel VBAファイルの操作[作業用ブックを開く]](http://officetanaka.net/excel/vba/file/file05.htm)
- [Excel VBA を学ぶなら moug モーグ | 即効テクニック | データ型を確認する](http://www.moug.net/tech/exvba/0140004.html)
- [VBA／マクロ便利Tips：VBAにおけるワークシート関数――INDEX、MATCH、VLOOKUPの使い方 - ＠IT](http://www.atmarkit.co.jp/ait/articles/1506/19/news015.html)
- [Dictionary（ディクショナリー）連想配列の使い方について｜ExcelマクロVBA技術解説](http://excel-ubara.com/excelvba4/EXCEL216.html)
- [メッセージを改行する - ダイアログ - Excel VBA入門](http://www.officepro.jp/excelvba/dialog/index4.html)
- [ファイル・ディレクトリが存在するか確認する - アプリケーションとしてのVBA](http://www.k1simplify.com/vba/tipsleaf/leaf243.html)
- [Office TANAKA - 番外編[存在しないパスのフォルダを一発で作成する]](http://officetanaka.net/other/extra/tips07.htm)
- [20007-0 | 「0&」の意味 - VB初心者友の会 - はじめたばかりの人専用Q&A掲示板過去ログ](http://www.gizcollabo.jp/vbtomo/log/archive/hajikariqa_20007_0.html)
- [SHCreateDirectoryEx function (Windows)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb762131(v=vs.85).aspx)
- [Office TANAKA - Excel VBA講座：ファイルの操作[テキストファイルを操作する]](http://officetanaka.net/excel/vba/file/file08.htm)
- [エクセルExcel大事典 VBAマクロ Open Line Input # Write Print Get Put Seek Width UnLock](http://home.att.ne.jp/zeta/gen/excel/c04p45.htm)
- [Office TANAKA - Excel VBA関数[MsgBox関数]](http://officetanaka.net/excel/vba/function/MsgBox.htm)
- [全ては時の中に… : 【Excel VBA】Printステートメントに改行コードを含めない方法](http://blog.livedoor.jp/akf0/archives/51597085.html)