Application.Runを起点にセルの値やユーザーフォームからマクロを呼び出す【ExcelVBA】

https://twitter.com/jhsshinrojimu/status/1452763148432252929?t=yyy2t7qL8ph7Xsz7hbhMfA&s=19

先日、ハイパーリンクをマクロ実行ボタン化するツイートが気になってたけどなかなか着手できてなくて出遅れ感がある中、先日のこの記事の中で`Application.Run`とセルの値でマクロを呼び出すということができたので他のパターンにも手札を増やしてみようという企画。

https://qiita.com/taleau/items/34371e793b83ec7141ee#applicationrun%E3%81%AB%E5%8F%AF%E8%83%BD%E6%80%A7%E3%82%92%E6%84%9F%E3%81%98%E3%81%9F


## 概要
ハイパーリンクに限らず、セルの値をプロシージャ名にしておけば各イベントをトリガーにマクロを実行できる、というもの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281933/2d914482-0217-a496-4c78-3a3da6b5373a.png)
ハイパーリンクをトリガーにする場合は適当に自セル参照とかさせておけばいい。


また、ユーザーフォームのあれこれにプロシージャ名を持たせておいてもいける。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281933/bc6e2572-ebae-ae30-8104-2343977384d5.png)


### 標準モジュール：全パターン共通で呼ぶやつ
``` vb
Option Explicit

Sub CallProc(ProcName)
  On Error GoTo Err
  Dim wbName: wbName = ThisWorkbook.Name
  Application.Run wbName & "!" & ProcName
  Exit Sub
Err:
  Select Case Err.Number
    Case Is = 1004 '何もしない。呼び出すものが無いときのエラーNoなので。
  End Select
End Sub

Sub test1()
  MsgBox 1
End Sub

Sub test2()
  MsgBox 2
End Sub

```

## シートモジュール

### `Worksheet_FollowHyperlink`から
``` vb
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
  Call CallProc(Target.Name)
End Sub
```

### `Worksheet_BeforeDoubleClick`から
``` vb
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
  Call CallProc(Target.Value)
End Sub
```

### `Worksheet_SelectionChange`から
``` vb
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
  Call CallProc(Target.Value)
End Sub
```

ちゃんと見てないけどセルの値を引数に持つやつなら何でもいける

## ユーザーフォーム

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281933/dc09f9a3-5180-e0a6-6815-9f4fb5af4a82.png)

### コマンドボタン：オブジェクト名を拾うパターン
``` vb:ボタンでいうと上のやつ
Private Sub test1_Click()
  Call CallProc(Me.test1.Name)
End Sub
```

### コマンドボタン：キャプションを拾うパターン
``` vb:ボタンでいうと下のやつ
Private Sub CommandButton1_Click()
  Call CallProc(Me.CommandButton1.Caption)
End Sub
```

### リストボックス
``` vb
Private Sub UserForm_Initialize()
  With Me
    .ListBox1.AddItem "test1"
    .ListBox1.AddItem "test2"
  End With
End Sub

Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
  With Me.ListBox1
    Dim myIndex: myIndex = .ListIndex
    Dim myItem: myItem = .List(myIndex)
    Call CallProc(myItem)
  End With
End Sub
```

### リストビュー
Proc名の列幅を極小にすればProc名を隠して略称だけ表示することもできる
（ListViewの構造がイマイチ理解できていないのでそのあたりはご愛嬌）

``` vb
    With .ColumnHeaders
      .Add 1, "key1", "略称", 50
      .Add 2, "key2", "Proc名", 50
    End With
```

``` vb

Private Sub UserForm_Initialize()
  Call SetListView
End Sub

Private Sub ListView1_DblClick()
  Dim buf
  buf = Me.ListView1.SelectedItem.ListSubItems.Item(1).Text
  Call CallProc(buf)
End Sub

Private Function myProcs_()
  Dim myArr(1)
  myArr(0) = Array("てすと１", "test1")
  myArr(1) = Array("てすと2", "test2")
  myProcs_ = myArr
End Function

Private Sub SetListView()
  Dim myProcs
  myProcs = myProcs_

  With Me.ListView1
    .View = lvwReport
    .LabelEdit = lvwManual
    .HideSelection = False
    .AllowColumnReorder = True
    .FullRowSelect = True
    .Gridlines = False
    
    With .ColumnHeaders
      .Add 1, "key1", "略称", 50
      .Add 2, "key2", "Proc名", 50
    End With
    
    Dim cnt
    For cnt = 0 To UBound(myProcs)
      With .ListItems.Add
        .Text = myProcs(cnt)(0)
        .SubItems(1) = myProcs(cnt)(1)
      End With
    Next
  End With
End Sub
```

## 妄想
### プロシージャリストを自動で取得することもできそう

こんなのを使えばあんなこともできるかも

https://excel-ubara.com/excelvba5/EXCELVBA269.html

### 他ブックのマクロも呼び出せそう

`wbName`に投げ込むブック名を他ブックにしてやれば他ブックのマクロも呼び出せそう(というか他ブックのマクロを呼ぶのが本来の用途？)

``` vb
  Dim wbName: wbName = ThisWorkbook.Name
  Application.Run wbName & "!" & ProcName
```

引数つきで渡す方法もここにあります。

https://excel-ubara.com/excelvba1/EXCELVBA427.html