「Change」イベント・・・セルの内容が変更された時にプロシージャを実行する。 WorkSheetオブジェクトで処理できるイベントのうち、今回は「Change」イベントの効率的な使い方を実践してみます。 ■「Change」イベントの活用
エクセルVBAの一連のプロシージャはどのタイミングで実行されるのでしょうか? 一般的には、フォームから選択した「ボタン」やツールバーに登録したボタンにマクロを登録し、実行時にそのボタンをクリックする方法が多いのではないかと思われます。 但し、連続して数値を入力して結果を呼び出すような場合に、いちいちボタンをクリックしてプロシージャを実行させるのは、面倒なだけではなく、作業効率も悪くなりますので、今回の「Change」イベントをうまく活用して実作業の効率アップを計りましょう。 ■Worksheetオブジェクトのイベント Changeイベント以外にも、シートに対して発生するイベントがあります。種類は下記表のとおりです。
■どのモジュールで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に対するコードウィンドウに記述します。)
日給者に対する、税金(源泉徴収税額)と支給額を計算します。 本来、上記表のような簡単なものなら、わざわざ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」の構文になっています。
|