ExcelVBAでControlオブジェクトのChangeイベントを一括制御する方法

ExcelVBAでControlオブジェクトのChangeイベントを一括制御する方法

コントロールオブジェクトがたくさんあったとして、それの Change イベント、全部ベタで書かなきゃいけないの…?と思って調べてみたら、とっても便利な方法があったのでシェアさせて頂きます。


コントロールの変数制御

過去に、コントロールオブジェクト自体の制御については書いたことがありました。

TextBox1, TextBox2, TextBox3 のような数値の部分を変数で制御すると、For~Nextなどで回せるようになって、ベタ打ちするよりずっと楽になるなーと思って重宝していたんですが。

Changeイベントを楽にできないものか…

今回、TextBox から内容が変わったときに走る、いわゆる Change イベントを全ての TextBox に実装したかったのですが、TextBox の数が多くて「これ全部に Change イベント書くのか…」とげんなりして、なんかスマートにできないかなーと調べてみたらこんな記事が。

できるのか!!すごい!参考にさせて頂いて実装してみたらすっごく省コードで出来ちゃいました!nishi6さんに、この場にて厚く御礼申し上げます。

せっかくなので、参考にさせていただいて、私なりにちょびっと変えたほうのコードも載せておこうと思います。一応解説も私なりに書いてみましたが、元記事のほうもとても参考になりますので、そちらも是非ご参照ください。

コード

Class1

まず、[挿入]→[クラスモジュール]。Class1に以下を書きます。

Private WithEvents Target As MSForms.TextBox
 
Public Sub SetCtrl(new_ctrl As MSForms.TextBox)
  Set Target = new_ctrl
End Sub
 
Private Sub Target_Change()
  If Target.Value = "" Then
    Target.BackColor = RGB(200, 200, 200) '背景を灰色に
  Else
    Target.BackColor = RGB(255, 255, 255) '背景を白に
  End If
End Sub

1行目のWithEventsでイベントを拾うことができるので、TextBox 型で宣言しておきます。

3~5行目は、Targetとなる TextBox をセットしています。

7~13行目が、実際に処理を行う Change イベントです。このコードでは変更のあった TextBox の中身が空なら背景を灰色、そうでなければ白にしています。

UserForm1

次に、対象のTextBoxがおいてあるユーザーフォームへ。

Private ctrl(1 To 30) As New Class1
 
Private Sub UserForm_Initialize()
  Dim i As Integer
  For i = LBound(ctrl) To UBound(ctrl)
    ctrl(i).SetCtrl Me("TextBox" & i)
  Next
End Sub

ユーザーフォームが呼び出されたとき、先程の3~5行目に書いた SetCtrl プロシージャを呼び出して、対象の TextBox をそれぞれ Target へ定義します。

これなら、TextBox がたくさんあっても、指定の番号のものだけ Change イベントを一括で制御できちゃいます。素敵だ!

チェックボックスでもできちゃう!

ちょこっと書き換えれば、CheckBox の Click イベントにも。

Class1

Private WithEvents Target As MSForms.CheckBox
 
Public Sub SetCtrl(new_ctrl As MSForms.CheckBox)
  Set Target = new_ctrl
End Sub
 
Private Sub Target_Click()
  If Target.Value = True Then
    'チェックボックスがオンのとき
  Else
    'チェックボックスがオフのとき
  End If
End Sub

UserForm1

Private ctrl(1 To 30) As New Class1

Private Sub UserForm_Initialize()
  Dim i As Integer
  For i = LBound(ctrl) To UBound(ctrl)
    ctrl(i).SetCtrl Me("CheckBox" & i)
  Next
End Sub

同じ感じにすれば、他のイベントやコントロールでもできそうですね!

ただ、WithEvents は Enter, Exit, BeforeUpdate, AfterUpdate など、コントロールのフォーカス移動時に発生するイベントは検知できないみたいですのでご注意を。

  • WithEventsでイベントを拾う – EXCEL-LENCE webhttp://www.excellenceweb.net/vba/class/userform_hook.html

でもすごく便利ですね。興奮してしまいましたw

追記:やってみて分かったのですが、TextBox に関しては Change イベントって使いにくいですね…! 一文字変わる毎に走ってしまうので、使い方によっては無用な処理を引き起こす可能性も…。

「テキストボックスが変更されたら」という処理は Exit とか AfterUpdate を使ったほうが良さそうです。ただ、上述したようにそのあたりは WithEvent では検知できないので、やろうとするにはもうちょっと工夫が必要みたい。

追記:別解

上記では配列を使ってコントロールの番号を指定していますが、ユーザーフォーム上の全てのコントロールをループして、その中の任意のタイプのコントロールだけ、ということもできます。

Class1への記述はそのままで、ユーザーフォーム側だけ変えます。

UserForm1

Private CheckCollection As Collection 'コレクションの宣言

Private Sub UserForm_Initialize() 'Formが開くとき
  Set CheckCollection = New Collection 'コレクション生成
  Dim ctrl As Control 'コントロール変数の宣言
  Dim obj As Class1 'インスタンス変数を宣言
  For Each ctrl In Me.Controls  'フォーム内のコントロールをループ
    If TypeName(ctrl) = "CheckBox" Then 'コントロールのタイプがチェックボックスだったら
      Set obj = New Class1 'インスタンスの生成
      obj.SetCtrl ctrl 'コントロールをセット
      CheckCollection.Add obj 'コレクションへ追加
      Set obj = Nothing 'インスタンス破棄
    End If
  Next
End Sub

番号関係なく全部のコントロールに対応させたい、という場合はこちらの使い方でも。

thomさんの記事を参考にさせていただいています!

関連

この WithEvent を使って、クリックイベントを拾うカレンダーコントロールを作ってみました。

クラスモジュールの使い方をまとめてみました。

公開日:2013/06/04
更新日:2017/04/05

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)

YouTubeでQ&Aコンテンツを企画しています

運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。