初心者のためのExcelマクロ超入門(絶対できるVBA開発)

マクロがまったくわからない人のためにエクセルマクロやVBAについてできるだけわかりやすく書いています。Twitter:@shuhhohhey

初心者のためのExcel エクセルマクロVBA入門-実践:セルが変更されたイベントを処理する(発注書マクロ)

値によってシートに表示する内容を変える

いろいろと考えてたけど結局いきあたりばったりです。どうも、僕です。では、前回のお話の通りさっそく作っていきましょう。まずは印刷ボタンが押される・・・前に会社を選んだら、その会社の商品で発注が必要な商品のリストがないと意味がないですよね。発注するかしないかを担当者が考えなければいけません。


つまり・・・

  • 会社を選択したら・・・
  • 在庫表からその会社の商品をすべて検索して
  • 発注が必要な商品をリストに出力する


なんて処理をします。問題は「会社を選択したら」ですよね。そしたら自動で処理が実行されなければいけません。これはどうしたらいいのでしょうか?実は、ちゃんとエクセルにはそういうイベントが用意されています。その名も・・・

Worksheet_SelectionChange

というイベントです。

セルの値が変更された時に実行されるイベント(Worksheet_SelectionChange)

このWorksheet_SelectionChangeはワークシートのどこかに変更があったら実行されるプロシージャ(イベント処理)です。イベント処理だからといって、( ゚д゚)?とか思う必要はありません。結局プロシージャの一種です。つまり・・・

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

End Sub

と書きます。この処理はイベント処理なので、会社を選択するセルがあるシートのオブジェクトに記載します。ついでにこのシートにもオブジェクト名を付けておきましょう。シートオブジェクトをオブジェクトブラウザから選択してプロパティを変更します。簡単です。(オブジェクト名)を変えただけです。


f:id:drumer2sh:20140407193822p:plain


こんな感じですね。こうするとシートもオブジェクトとしてぐっと扱いやすくなります。後はいつも通りです。さっきの日本語記載しておきましょう。

Option Explicit

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 会社名が選択された時に処理されるイベント
' ここに具体的な処理は書かない
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    
    ' 在庫表からその会社の商品をすべて検索して
    ' 発注が必要な商品をリストに出力する
    
    ' ような処理をここから呼ぶ。
    
End Sub

これでいいですね。変数宣言を強制するOption Explicitも忘れずに。後は日本語をVBAに変換してやるだけです。簡単ですね。

Option Explicit

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 会社名が選択された時に処理されるイベント
' ここに具体的な処理は書かない
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    
    ' 在庫表からその会社の商品をすべて検索して
    ' 発注が必要な商品をリストに出力する
    
    ' ような処理をここから呼ぶ。
    Call 標準モジュール名.outputOrderProduct(Target.Value)
    
End Sub

基本はこれだけです。後は標準モジュールにこのoutputOrderProduct(Target.Value)を作ればよいのです。なぜこのように処理を色々と分けているのか?ということについては、リンク(http://shuhho.hatenablog.com/entry/excelvba-28)こちらを見てください。とても大切なことです。

処理を分けることで、修正が楽になる!


これはある種真実です。5行~10行のプログラムならすぐ読めますが、300行のプログラム・・・読む気になります?このように処理を短く区切っておくと、かなり楽になるのです。

良く見るとなんか変?Worksheet_SelectionChangeの特徴


さて、ここでちょっと考えてみましょう。Worksheet_SelectionChangeイベントプロシージャはシートのセルの内容が変更されたら実行されるわけですよね?ということは、会社名を変更したら?当然このイベントが実行されます。数量が変更されたら?やっぱりこのイベントが実行されます。つまりWorksheet_SelectionChangeは、シート内のどこのセルが変わっても実行されてしまうイベントなのです。これでは、とっても困ります。。

Worksheet_SelectionChangeで特定のセルが変わった時にだけ実行したい。


どうするのかは、わかりますよね?そうif文を使えばいいのです。if文で会社名のセルだけが値が変わった時に実行するようにすればいいのです。では変更されたセルがどこか?なんてどうやってわかるのでしょうか?それは実はWorksheet_SelectionChangeの引数が持っています。引数Targetは変更されたセルのRangeオブジェクトを持っているのです。これを使えば判定できそうですね。ではいつも通り日本語で書きましょう。

Option Explicit

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 会社名が選択された時に処理されるイベント
' ここに具体的な処理は書かない
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    
    ' 変更されたのは会社名のセルなのか?
        ' 変更されたのが会社名だったら・・・
        
        ' 在庫表からその会社の商品をすべて検索して
        ' 発注が必要な商品をリストに出力する
        
        ' ような処理をここから呼ぶ。
        Call 標準モジュール名.outputOrderProduct(Target.Value)
    
    ' それ以外のセルの時は何もしないよ
    
End Sub

となりますね。ではこれをif文で表現しましょう!っと?またここで困りましたね。引数Targetが会社名のセルなのか?ってどうやって調べれば良いのでしょうか?これにはIntersect関数を利用します。

Intersect関数-あるセルがセル範囲に含まれるかどうか

これを使うとあるセルが、特定のセル範囲に含まれるかどうかを「なんかごにょごにょして」結果を返します。書式は以下の通りです。

Application.Intersect(調べたいセル範囲, 含まれるかどうかの範囲)

範囲に含まれなければNothingを返します。覚えてますか?Nothing。。。Find関数でもやりましたね。つまり、Intersect関数の結果がNothingじゃないならセル範囲に含まれていることになります。今回は、引数Targetが、会社名を入力しているセルなら良いので・・・

Option Explicit

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 会社名が選択された時に処理されるイベント
' ここに具体的な処理は書かない
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    
    ' 変更されたのは会社名のセルなのか?
    If Not Application.Intersect(Target, shtOrderTool.Range("G8")) Is Nothing Then
    
        ' 変更されたのが会社名だったら・・・
        
        ' 在庫表からその会社の商品をすべて検索して
        ' 発注が必要な商品をリストに出力する
        
        ' ような処理をここから呼ぶ。
        Call outputOrderProduct(Target.Value)
    
    ' それ以外のセルの時は何もしないよ
    End If
    
End Sub

こうなります。イベントプロシージャはシートオブジェクト内に書くので、shtOrderToolというシートオブジェクト名は省略できますが、最初のうちは絶対書きましょう。いきなりRangeとか書いても結局どこの範囲?かがまったくわからないので。このIf文のNotの使い方は今までも扱っているので詳しく言わないですが、Nothingかどうか?には=ではなくて「Is」を使う所は注意しましょう。

さて、こうすると・・・日本語直訳すると

TargetがセルG8に含まれるかを関数Intersectで計算し、その結果がNothingじゃなかったら


となります。とってもややこしいですが、慣れれば結構簡単です。これで、変更されたセルが会社名のセル以外は何もしないようになりました。後はG8ってところなんですが・・・

レイアウト変更に強い!名前の定義


セルG8に名前を付けてしまいます。今回は「CorprateName」という名前を付けました。これで・・・

Option Explicit

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 会社名が選択された時に処理されるイベント
' ここに具体的な処理は書かない
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    
    ' 変更されたのは会社名のセルなのか?
    If Not Application.Intersect(Target, shtOrderTool.Range("CorprateName")) Is Nothing Then
    
        ' 変更されたのが会社名だったら・・・
        
        ' 在庫表からその会社の商品をすべて検索して
        ' 発注が必要な商品をリストに出力する
        
        ' ような処理をここから呼ぶ。
        Call outputOrderProduct(Target.Value)
    
    ' それ以外のセルの時は何もしないよ
    End If
    
End Sub

となります。こうすることで、何が起こるかというと、、、、このシートのレイアウトが変わって会社名のセルがG8からH9へ変更になってもプログラムを直す必要がないのです。ただ名前の定義をH9へ移動すればいいだけですね。プログラムは直さないのでとても簡単です!このようにエクセルの元からの機能を使うことで、ぐっと効率的にできるのです。

続きは次回で!outputOrderProductプロシージャを作っていきましょう!


今日はここまで!



かしこ。