| Home |
「Change」イベント・・・セルの内容が変更された時にプロシージャを実行する。
WorkSheetオブジェクトで処理できるイベントのうち、今回は「Change」イベントの効率的な使い方を実践してみます。

■「Change」イベントの活用
エクセルVBAの一連のプロシージャはどのタイミングで実行されるのでしょうか? 一般的には、フォームから選択した「ボタン」やツールバーに登録したボタンにマクロを登録し、実行時にそのボタンをクリックする方法が多いのではないかと思われます。
但し、連続して数値を入力して結果を呼び出すような場合に、いちいちボタンをクリックしてプロシージャを実行させるのは、面倒なだけではなく、作業効率も悪くなりますので、今回の「Change」イベントをうまく活用して実作業の効率アップを計りましょう。

■Worksheetオブジェクトのイベント
Changeイベント以外にも、シートに対して発生するイベントがあります。種類は下記表のとおりです。
イベント 発生するタイミング
Activate ワークシートがアクティブになった時
BeforeDoubleClick ワークシートをダブルクリックした時
BeforeRightClick ワークシートを右クリックした時
Calculate ワークシートを再計算した時
Change セルの値が変更された時
Deactivate ワークシートが非アクティブになった時
FollowHyperlink ワークシートのハイパーリンクをクリックした時
SelectionChange
(規定のイベント)
ワークシートで選択範囲を変更した時



■どのモジュールでChangeイベントのコードを記述するのか?
イベントプロシージャはオブジェクト(シートやブック)に対して、色々なイベントを発生させますので、一般のプロシージャ(標準モジュール)に作成するのではなく、オブジェクトに対して直接作成する形式になっています。
仮に「Sheet1」上にイベントプロシージャを作成したければ、「Sheet1」のコードウィンドウにプロシージャを記述する事になります。
※VBE(Visual Basic Editor)の画面構成↓


■イベントプロシージャの為にコードウィンドウを開きます。
方法その1・・・シートの見出しから開きます。(例として、Sheet1を右クリック→ショートカットメニューから「コードの表示(V)」をクリックします。)


方法その2・・・プロジェクトエクスプローラーを開き、該当するシートをダブルクリックします。
※プロジェクトエクスプローラーを開くには・・・シートのツールバーから、ツール(T) → マクロ(M) → Visual Basic Editor(V)をクリックします。
又はシート上で「Alt + F11」のショートカットキーを押す事でもプロジェクトエクスプローラーを開くことができます。





■開いたコードウィンドウにプロシージャを記述します。

まず、コードウィンドウの[オブジェクト]ボックスから「Worksheet」と[プロシージャ]ボックス「Change」を選択します。
但し、「Worksheet」を選択した時に、規定のイベントである、「SelectionChange」が自動的に選択される場合がありますが、その際は同じプルタブから「Change」イベントを選択し、不要の「SelectionChange」イベントのコードは削除しましょう。



尚、タイトルのPrivate Sub Worksheet_Change(Byval Target As Range)はユーザーが任意に決めるものではなく、VBAの決まりに従って自動的に付けられる事になっています。
又、Subステートメントの前にPrivateキーワードが付きますが、これはイベントプロシージャの仕様として、必ず付ける決まりになっています。ちなみに、Privateキーワードが付いたプロシージャは同じモジュール内にあるプロシージャからしか呼び出せません。また、ツールバーボタンに登録したり、VBEの実行ボタン(sub/ユーザーフォームの実行)から実行する事もできません。



■標準モジュールとの違いを確認しましょう。
例として、同じ内容のプロシージャを「Sheet1」と標準モジュールのコードウィンドウに各々記述し、その実行のタイミングの違いを確認してみます。
1、「Sheet1」に対するイベントプロシージャとして記述しました。↓


└→「Sheet1」の任意のセルに(例としてB2)数字1を入力し、Enterキーを押します。(↓キーでも可)
※プロシージャが実行されています。



2、同じ内容のプロシージャを標準モジュールで実行してみます。↓


└→「Sheet2」のセルに(例としてB2)に数字1を入力し、Enterキーを押します。(↓キーでも可)
※プロシージャは実行されません。


標準モジュールでの実行には「Visual Basic Editor」のツールバーから「sub/ユーザーフォームの実行」をクリックするか、シート上のツールバーから、マクロ(M)→マクロ(M)→マクロ名の選択→(Alt+F8でも可)[実行]ボタンをクリックする事になります。



■実践編(日給者に対する税額を求めるイベントプロシージャの作成)
管理人が実際に業務に使用しているプロシージャを簡略化してご紹介します。

下のシートで、時間の行、セルC2:G2に労働時間を入力すると、C3:G3には時間×時給(この場合@750)、C4:G4には対応する税額、C5:G5には差し引き支給額が計算され表示される仕組みになっています。
尚、予め3行目のセルには「2行目のセル×750」が、5行目のセルには「3行目-4行目」の数式が入力されているものとします。
VBAの役割としては、3行目の「課税支給額」の値を基に、下の「日額表」からVLookup関数を使って該当する税額を4行目に表示することになります。
また、Changeイベントと、ユーザー定義関数を組み合わせて、一定のセル範囲(この場合C2:G2)の値が変更された時だけ、プロシージャが実行されるようにしています。



※プロシージャ例(Sheet1に対するコードウィンドウに記述します。)
1

2



3
4
5

6

7

8

9


10


11


12

13

14


15

16

17
18
19

20
Private Sub Worksheet_Change(ByVal Target As Range)

Dim myarea As Range
Dim z As Variant
Dim i As Integer

Set myarea = Range("C2:G2")
If chk(Target, myarea) = True Then
ActiveCell.Offset(1).Select

i = ActiveCell.Offset(-1).Value

z = Application.VLookup(i, Range(Cells(11, 1), Cells(36, 3)), 3, True)

Select Case i

Case Is < 2900
ActiveCell.Value = 0

Case 2900 To 5099
ActiveCell.Value = z

Case Is > 5099
ActiveCell.Value = "範囲外"

End Select

End If

End Sub


Private Function chk(trg As Range, myrng As Range) As Boolean

Dim tmp As Variant

chk = False
Set tmp = Application.Intersect(trg, myrng)
If Not tmp Is Nothing Then chk = True

End Function




1
2
3
4
5
6
7
8
9
10
11
12
13
14

15

16
17
18
19
20
Changeイベントに対するプロシージャを記述します。
変数を宣言します。
Setステートメントを使い、変数myareaにセル範囲(C2:G2)を代入します。
もし、サブルーチン「chk」がTrueを返したら、
アクティブセルの一つ下のセルを選択します。(4行目のセル)
変数 i に、アクティブセルの一つ上の値を代入します。(この場合、3行目の課税支給額)
変数zにVLookup関数で求めた値を代入します。
Select Caseステートメントで、変数 i に対する条件分岐を行います。
i が2900未満の場合、アクティブセルに0を代入します。
i が2900以上、5099以下の場合、アクティブセルに変数zの値を代入します。
i が5099を超える場合、アクティブセルに"範囲外"の文字列を代入します。
Select Caseステートメントを終了します。
4で開始したIfステートメントを終了します。
プロシージャの記述を終了します。

chkというユーザー定義関数を記述します。引数として、オブジェクト型の[trg]と[myrng]を指定します。又、chkの結果として、Boolean型(TrueまたはFalse)を指定しています。
変数を宣言します。
chk(ユーザー定義関数)にFalseを代入します。
Setステートメントを使って、変数tmpに Application.Intersectメソッドを代入します。
変数tmpにNothingが代入されていない場合、(重複がある場合)Trueを返します。
ユーザー定義関数の記述を終了します。



日給者に対する、税金(源泉徴収税額)と支給額を計算します。
本来、上記表のような簡単なものなら、わざわざVBAを使用する事なく、ワークシート上の関数で十分対応は可能です。ただし、実際の税額の計算には、扶養親族等の人数・勤務形態(甲・乙・丙)の区分による税率の違い、また一定額以上は料率の算出方法が違う等、かなり複雑な分岐になりますので、ワークシート上の関数だけでは十分な対処は難しいと思われます。
※今回はChangeイベントのテストの為に、簡略化した表にしています。
さて、上のプロシージャの場合、セルC2:G2に勤務時間を入力し、[Enter]キー、又は[↓]キーを押した時にプロシージャが実行される様にする事がポイントになります。
その為に、「Changeイベント」と、サブルーチンである、「ユーザー定義関数」を組み合わせて作成されています。

■ユーザー定義関数(Functionプロシージャ)
Functionプロシージャは結果を返すプロシージャです。その特性を生かして、Excelでは独自のワークシート関数として利用することできます。
Functionプロシージャの構文
  Function 関数名(引数 As データ型)As データ型 ※データ型を省略した場合はVariant型になります
    Functionステートメント(結果を出すための処理)
    関数名=結果
  End Function

■Intersectメソッド(複数の範囲で重複する部分だけを選択する)
オブジェクト.Intersect(セル範囲1,セル範囲2,・・・,セル範囲n)
→ 引数として渡した複数のセルのうち、重複する部分があれば、その部分へのセル参照を返します。
重複する部分がない場合は、「Nothing」が代入される事になります。
※例: 2つの選択範囲から重複する部分があるかどうかチェックします。↓

Sub intercheck()

Dim ints As Variant
Dim myrng As Range

Worksheets("Sheet1").Activate
Set myrng = Range("A1:C5")
Set ints = Application.Intersect(myrng, ActiveCell)
If ints Is Nothing Then
MsgBox "共通部分はありません。"
Else
MsgBox "共通のセルは " & ActiveCell.Address & " です。"
End If
End Sub


↑myrng(セルA1:C5)と選択したセル(ActiveCell)が重なっていれば、そのセルのアドレスをメッセージボックスで表示します。
重なる部分がない場合は、(Nothingが返されたら)"共通部分はありません。"とメッセージボックスで表示します。

1、重複する部分がある場合(セルA1:C5は分かりやすい様に色を付けています)


2、重複する部分がない場合


※上のユーザー定義型関数「chk」の役割は、2つのセル範囲を比較して、重複する部分があれば「True」を、無ければ「False」
を返す、という内容になります。
それをChangeイベントの4でサブルーチンとして呼び出し、Target と myarea(C2:G2)の重複部分のチェックをさせています。
タイトルの、Private Sub Worksheet_Change(ByVal Target As Range)の引数 Target には値の変更されたセルがObject型で格納されています。
尚、親プロシージャとサブルーチンの引数名は必ずしも一致する必要はありません。但し原則として引数の個数は同一でなければいけません。(引数の個数がどうしても一致しない場合はOptionalキーワードを使用します。)

さて、今回はChangeイベントの実践のためにユーザー定義型関数を使用しましたが、これを使用する事なくプロシージャを作成することも可能です。↓

Private Sub Worksheet_Change(ByVal Target As Range)

Dim myarea As Range
Dim z As Variant
Dim i As Integer

Set myarea= Range("C3:G3")
Set ints = Application.Intersect(myarea, ActiveCell)
If ints Is Nothing Then
Exit Sub
Else

ActiveCell.Offset(1).Select
i = ActiveCell.Offset(-1).Value
z = Application.VLookup(i, Range(Cells(11, 1), Cells(36, 3)), 3, True)

Select Case i

Case Is < 2900
ActiveCell.Value = 0

Case 2900 To 5099
ActiveCell.Value = z

Case Is > 5099
ActiveCell.Value = "範囲外"

End Select
End If
End Sub

このように、ユーザー定義型関数を使用しなくてもプロシージャは成り立ちますが、上記のChangeイベントの場合で言えば、選択範囲が複数あり、その結果、同じ処理を繰り返したりする場合には、その部分をパーツ化した方が、より分かりやすく見やすい構文になると思われます。また、複雑なプロシージャの場合などは、パーツとして配置する事によって、エラーの特定(デバッグ処理)やタイプミスの防止など、メリットは大きいと思いますので、積極的に活用していきましょう。

■VLOOKUP関数(範囲の左端の列で検索値を検索し、値の見つかった行の列位置のセルの内容を表示する。)
  =VLOOKUP(検索値,範囲,列位置,検索の型)
※検索の型(検索範囲の値は昇順に並び替えておく必要があります)
└→ "TRUE"もしくは省略、または、"1"と入力しても可。=検索値が見つからない場合は、検査値未満でもっとも大きい値が適用されます。
└→ "FALSE"または"0"と入力しても可。=検査値と完全に一致する値だけが検索され、見つからない場合はエラー値の#N/Aが返されます。

◎VLOOKUP関数をVBAで使用する場合は、エラーに対する注意が必要です。
1、Application.WorksheetFunction.VLookup(検索値, 範囲, 列位置, 検索の型) の場合の例↓

Sub myvlookup_01()
Dim i As Integer
  Worksheets("Sheet1").Activate
    i = Application.WorksheetFunction.VLookup(Range("D4"), Range("A4:B9"), 2, False)
    Range("E4").Value = i
End Sub

※セルD4に商品番号を入力して、[売価検索]ボタンをクリックすると、上記 myvlookup_01 のプロシージャが実行される様に登録してあります。

↑商品番号300の売価3,050がセルE4に表示されました。


                   ↓↓↓

↑完全に一致する検索値がない場合、エラーになりますので、下のプロシージャの様にエラー処理を分岐させます。

Sub myvlookup_02()
Dim i As Integer
  On Error GoTo Err
    Worksheets("Sheet1").Activate
    i = Application.WorksheetFunction.VLookup(Range("D4"), Range("A4:B9"), 2, False)
    Range("E4").Value = i
    Exit Sub
  Err:
  Range("E4").Value = "該当なし"
End Sub

※エラー処理で分岐させた結果。↓



2、i = Application.VLookup(検索値, 範囲, 列位置, 検索の型, 2, False) の場合の例↓

Sub myvlookup_03()
Dim i As Variant
  Worksheets("Sheet1").Activate
  i = Application.VLookup(Range("D4"), Range("A4:B9"), 2, False)
  If IsError(i) Then i = "該当無し"
  Range("E4").Value = i
End Sub

プロシージャ myvlookup_03 を実行した結果。↓エラー処理をせずに対応しています。
注意する箇所としては、「Application.WorksheetFunction.VLookup」ではなく、「Application.VLookup」の構文になっています。



▲Top