ロゴ(青) Excel/VBA Tips ロゴ(緑)

  【 擬似からの脱却 】  [ Breakthrough in the Pseudo-Control Arrays ]

[ clsBpca の 軌跡 ] [ 前頁 , 次頁 , §1 , §2 , §3 , §4 , §5 , §6 , §7 , §8 , §9 , §10 , §11 , §12 ]
[ 汎用クラス , トグルラベル クラス , Focus クラス , クラス アドイン , カレンダークラス ] [ 質問はこちら]

  ====================================================================
  『擬似からの脱却』 汎用クラスモジュール [ clsBpca ]   (Ver 3.1)
  ====================================================================

 

このクラスはx64 でも動作します
Ver 2.0 より、Excel97 はサポート対象外となりました( Excel97 でも利用可能な Ver 1.4 は こちら です)。


この『擬似からの脱却』汎用クラス [ clsBpca ] は下記のコントロールとイベントをサポートしています。
  -- Controls --
    Label , TextBox , CommandButton , OptionButton , CheckBox
    ComboBox , SpinButton , ToggleButton
    標準コントロールの内、「ListBox, Frame, TabStrip, MultiPage, ScrollBar」 を除外してますが、その理由は
    「これらの コントロール が コントロール配列にするくらい数多く配置される事がない為」です。
 -- Events --
    Enter , Exit , BeforeUpdate , AfterUpdate
    Change , Click , DblClick , KeyDown , KeyPress , KeyUp
    MouseMove , MouseDown , MouseUp , SpinDown , SpinUp , DropButtonClick
    FakeExit

※ Ver 3.0 で FakeExit イベントを追加しました。
  Frame / MultiPage 内 から抜け出す フォーカス 移動の際には、移動元の コントロール の Exit イベントが発生
  しません(後で遅れて発生します)。 その代わりとなる FakeExit イベント を追加しました。 [ 2016/10/11 ]

※ Ver 2.0 より、Enter/Exit/BeforeUpdate/AfterUpdate が利用可能になりました。
  通常、VBAのクラスモジュールでは、MsForms コントロールの Enter/Exit/BeforeUpdate/AfterUpdate の
  イベントを扱う事ができません。 今回、API ( ConnectToConnectionPoint ) を利用する事で、イベントを受け
  取れるようになりました。 後述  [ Ver 2.0 リリース : 2014/8/11 ]



このクラスの使用方法は下記サンプルブックの「利用例フォーム」と、後述のリファレンスガイドを
参考にして下さい。
本来の「配列化」するという目的の他に、『動的に追加したコントロール』のイベント処理にも
利用できます。

clsBpca コンセプト解説図


[ clsBpca ] クラス定義 および サンプル ブック [ Bpca_Class_V31.zip ] (192KB) 
          (注) 「一般的にダウンロードされておらず 〜」 というメッセージが出たら

        (配布ファイル) Bpca_Class_V31.zip
                              +-- Bpca_Class_V31.xls  (クラス定義 および サンプルブック)
                              +-- ExportV31  (エクスポート ファイル)
                                      +-- clsBpca.cls
                                      +-- clsBpcaCh.cls
                                      +-- modBpcaConst.bas
                                      +-- frmCalendarTool.frm    ( カレンダーフォーム 利用例 参照 )

    ( 2004/6/23 Ver 1.1 )
    ( 2005/3/11 Ver 1.2 )        解説文・クラスソース・サンプルソースにおいて、オブジェクト定義時での[New]指定
                                           を止めて、Initialize 時にインスタンス生成(Set ステートメント)するように修正しました。
    ( 2011/4/15 Ver 1.3 )        クラスの機能変更はありません。サンプルで使っている祝日マクロを最新版へ差し替えただけです。
                                           (Item プロパティを【既定のプロパティ】にしました。記載漏れ 2014/7/22)
    ( 2014/7/22 Ver 1.4 )      a) 各プロパティで Index 引数のエラーチェック(>0)が抜けていたのを修正しました。
                                             getIndex プロパティを追加しました。
                                         b) サンプルで使っている祝日マクロを最新版に差し替えました(山の日 対応済)。
    ( 2014/8/11 Ver 2.0 )      a) Enter , Exit , BeforeUpdate , AfterUpdate イベントのサポートを追加しました
                                         b) Event 引数の列挙型 [ BPCA2_Event ] を [ BPCA_Event ] と改め、Rgst メソッドの
                                             引数も Long 型から列挙型 [ BPCA_Event ] に変更しました。
                                             これにより、Event 引数が インテリセンス で簡単に指定できるようになります。
                                         c) 対象外のコントロール種別が Add メソッドで受け取り可能(イベントは発生しない)に
                                             なっていたのを修正しました。
                                         d) Excel97用 のクラスモジュール提供を取り止めました。以前の Ver 1.4 は こちら です。
    ( 2016/10/11 Ver 3.0 )   a) コンテナコントロール脱出時のExit未発生に対処するFakeExitを追加しました
                                         b) InitFColor / InitBColor プロパティを追加しました

    ( 2016/10/13 Ver 3.1 )   a) MultiPage/Frame にActiveControlが無い(ActiveControl=Nothing)場合に
                                             FakeExit イベント処理で実行時エラーになる不具合を修正しました



このクラスモジュールを利用する方法は 2つ あります。
    (1) 各自のワークブック内にクラスモジュールを取り込んで利用する。
            上記の配布ファイル ( Bpca_Class_V30.zip ) にクラスモジュールのエクスポートファイル
            が用意してあります。そのエクスポートファイルを各自のワークブックに【インポート】
            取り込んでください
            (注意事項)
              VBE のコードウィンドウ内で、Bpca_Class_V30.xls から 各自のワークブックへ
              コピー&ペーストによりクラスモジュールをコピーしないでください。
              コピー&ペーストでクラスモジュールを取り込むと、以下の機能が働きません。( 後述 )
                  a) Item プロパティを『既定のプロパティ』にする。
                  b) Enter, Exit, BeforeUpdate, AfterUpdate の4つのイベント。


    (2) クラスモジュールのアドインブックを利用する。
            汎用クラス(clsBpca) と他に幾つかのクラスモジュールを一緒にして アドインブック
            としても提供しています。
            この場合は、各自のワークブックにクラスモジュールを取り込む必要はありません。
            VBEの「ツール/参照設定」でアドインブック( ktClassAddin.xla ) を参照設定する
            だけで利用可能になります。各自のワークブック内に取り込む場合とで、利用上の
            違いはありません(クラスインスタンスの生成方法のみ異なります)。
                クラスアドイン Ver 3.10  (clsBpca Ver 3.0 を収録)
                    [ 2005/3/8 Ver 1.00 ]
                    [ 2014/8/11 Ver 3.00 ]
                    [ 2016/10/11 Ver 3.10 最新版で再構築 ]



=======================================================================
  「擬似からの脱却」汎用クラス [ clsBpca ]    イベント/メソッド/プロパティ一覧
=======================================================================
  a) このクラスを利用するには下記3モジュールをインポートで各自のワークブックに
    取り込む必要があります(クラスアドインを利用する場合は不要です)。
      ・ clsBpca & clsBpcaCh (クラスモジュール)
      ・ modBocaConst (「イベント種別」定数を定義した標準モジュール)定義内容はこちら

  b) [ clsBpcaCh ] クラスは、[ clsBpca ]クラスの下位クラスです(ユーザーサイドで
    直接このクラスを使うことはありません)。

  c) このクラスは Excel2000 以上で利用できます( x64 でも動作します)。


下記で[ object ]の部分には、clsBpca クラスを定義したオブジェクト変数を記述します。

定  義
UserForm モジュールの宣言セクション(モジュールの先頭)で、下記のように定義します。

    Private  WithEvents  object  As  clsBpca

    1UserForm内/1ブック内で、[ object ] を変えて複数定義しても構いません。

    ( object )
      任意の名前を指定して下さい。

    [ Initialize ] イベントプロシジャーにて下記のようにしてクラスのインスタンスを作成します。
        Set  object  =  New  clsBpca

    アドイン化したクラスを利用している場合は、New キーワードを使わずに下記のようにします。
           Set  object  =  CreateBpca ( )
      (CreateBpca 関数は、クラスアドイン で用意してある関数です)
 

メソッド
object .Add    MsForms コントロール オブジェクト

    コントロール配列化するコントロールオブジェクトを[ clsBpca ]クラスに追加するメソッドです。
    [ object ]グループとして配列化するコントロール全てに対して、 Add メソッドを繰り返し、最後に Rgst メソッド
    で登録します。

    通常は、ひとつの[ object ]に登録するコントロールは、全て同じ種別(TextBox , CommandButton 等)しか
    登録できませんが、Rgst メソッドでAllType 引数に True を指定すると、異なる種別のコントロールを一緒
    に登録できます(Rgst メソッドは、Add メソッドの後に実行しますので、Add メソッドの時点では異なる種類の
    コントロールを指定してもエラーにはなりません) 。

    Add メソッドで登録した順に、コントロール番号が 1 から割り当てられます。この番号が、各種イベント/プロ
    パティ において [ Index ] として扱われます。

    ( MsForms コントロール オブジェクト )
      コントロール配列化する、コントロールオブジェクトを指定します。
 

object .Rgst    Event , [ AllType ]

    Add メソッドで追加したコントロールの登録処理を行なうメソッドです。Add メソッドを繰り返した後に
    行ないます。

    AllType 引数を 省略 もしくは False 指定の場合、異なる種別のコントロールが混在していると、Rgst
    メソッドは失敗します(以後、全てのメソッド/プロパティを呼び出しても何も実行されません)。

    ( Event )
      発生させたい[イベント]について、対応する[ イベント種別コード ]の合計で指定します。
          例:  object.Rgst  BPCA_Change + BPCA_Click
      イベント種別コードの一覧は後述

      (補足)
      Ver 2.0 から、Event 引数 が 列挙型になりました(アドインクラスでは以前から)。
      これにより、マクロ記述の際に、インテリセンス によって、一覧から選択できるようになりました。
      これに伴い、従来、「BPCA_xxx (Long 型) ・ BPCA2_xxx(列挙型) 」であったものを、Ver 2.0 からは
      これを逆にして、『BPCA_xxx(列挙型) ・ BPCA2_xxx(Long 型)』 とします。
      なお、BPCA_xxx と BPCA2_xxx のどちらでも問題なく動作しますので、既に作成してあるマクロを
      修正する必要はありません。


    ( AllType )
      異なる種別のコントロールを混在させる場合は True を指定します。
      通常は 省略( =False )します。
 

object .Clear 

    クラスの解放などの終了処理です。
    UserForm の[Terminate ]イベントで必ず実行して下さい。


下記メソッドは、[ clsBpcaCh ] クラスが、発生したイベントを[ clsBpca ] クラスへ通知する為に使うメソッドです。
ユーザーサイドで使う事はありません。

    RaiseEnter , RaiseExit , RaiseBeforeUpdate , RaiseAfterUpdate ,
    RaiseChange , RaiseClick , RaiseDblClick ,
    RaiseKeyDown , RaiseKeyPress , RaiseKeyUp ,
    RaiseMouseMove , RaiseMouseDown , RaiseMouseUp ,
    RaiseDropButtonClick , RaiseSpinDown , RiseSpinUp
    Exit4FakeExit  

プロパティ
object .Count    [ = Integer ]

    [ object ]グループのコントロール配列に登録されているコントロールオブジェクトの数を返します。
    値の取得のみ可能です。

    ( 値の取得 )
      Add メソッドで登録したコントロールの数を数値で返します。
      この値は「コントロール 番号」の最大値でもあります。
 

object .InitFColor    ( Index )  [ = Long ]      (Ver 3.0 で追加)
object .InitBColor    ( Index )  [ = Long ]      (Ver 3.0 で追加)

    [ object ]グループの コントロール配列内で、Index で指定した コントロールについて、初期文字色(ForeColor)
    および 初期背景色(BackColor) を返します。
    値の取得のみ可能です。

    ( Index )
      コントロール番号(Add メソッドで追加した順に1 からの連番)を指定します。

    ( 値の取得 )
      指定の コントロールについて、初期文字色(ForeColor) および 初期背景色(BackColor) を返します。
      未登録名の場合は [ -1 ] を返します。

    初期文字色/初期背景色は Rgst メソッド実行時に、登録した個々の コントロールから取得しています。
    コントロールの色を変更する(エラー または フォーカス等) といった処理を行なっている場合に、元の色へ
    戻す際に利用してください。
        With WeekBtn
            .Item ( Index ) .BackColor = .InitBColor ( Index )
        End With
 

object .getIndex    ( CtrlName )  [ = Integer ]      (Ver 1.4 で追加)

    [ object ]グループのコントロール配列に登録されているコントロールオブジェクトの中から、
    コントロール名に対する Index の値(コレクション内で登録されている位置番号) を返します。
    値の取得のみ可能です。

    使用例:  Dim Index As Long
                Index = WeekBtn .getIndex ( "cmdSun" )
                WeekBtn .Item ( Index ) .BackColor  =  vbRed

    ( CtrlName )
      コントロール名を指定します。

    ( 値の取得 )
      コントロール名から、各プロパティで必要な Index の値を返します。
      未登録名の場合は [ -1 ] を返します。
 

object .EnableEvents    ( Index ) [ = Boolean ]

    [ object ]グループのコントロール配列内で、Index で指定したコントロールについて、イベント発生の
    可否を制御します。
    値の設定と取得が可能です。

    一時的に、イベントの発生を抑止したい場合には、その処理の直前で[ False ]を設定し、処理終了後に
    [ True ]で再設定します。

    例えば、Change イベントの処理内で、同一グループの他のコントロールの値を変更したい場合(当然、
    通常なら、その際に、変更したコントロールに対して Change イベントが発生します)などに利用します。
    他のコントロールの値を変更する前に、そのコントロールの EnableEvents を Flase に設定し、値変更後
    に、True に戻すことで、値を変更した際に、そのコントロールの Change を抑止できます。

    ( Index )
      コントロール番号(Add メソッドで追加した順に1 からの連番)を指定します。

    ( 値の設定 )
      指定のコントロールについて、イベント発生可否を、True / False で設定します。
      False を設定した後には、必ず True に戻す処理も加えておいて下さい。

    ( 値の取得 )
      指定のコントロールについて、現在のイベント発生可否状態が、True / False で返ります
 

object .EventState    [ = Variant ( Array 関数配列 ) ]

    [ object ]グループに対して、Rgst メソッドで指定した『イベント種別』を Array 関数による
    Variant 配列で返します。
    1 以降の添字には、各イベント発生指示の有無が True/False で入っています。
    値の取得のみ可能です。

    ( 値の取得 )
      [ 0 ] は Long型、[ 1 〜 17 ] は Boolean型

      [ 0 ] には、 Rgst メソッドの Event 引数で指定した数値が入っています。

      [ 1 〜 17 ] には下記イベントの発生指示有無が True/False で入っています。
      [ 1 ] Change , [ 2 ] Click , [ 3 ] DblClick
      [ 4 ] KeyDown , [ 5 ] KeyPress , [ 6 ] KeyUp
      [ 7 ] MouseMove , [ 8 ] MouseDown , [ 9 ] MouseUp
      [ 10 ] DropBtnClick , [ 11 ] SpinDown , [ 12 ] SpinUp
      [ 13 ] Enter , [ 14 ] Exit , [ 15 ] BeforeUpdate , [ 16 ] AfterUpdate
      [ 17 ] FakeExit

      (13 〜 16 は Ver 2.0 で追加されました。 17 は Ver 3.0 で追加されました。)
 

object .Item    ( Index ) [ = Object ]    【既定のプロパティ】

    [ object ]グループを構成する コントロールオブジェクトを返します。
    値の取得のみ可能です。
    (補) [値の取得のみ]とは、そのコントロールオブジェクトへの参照の事であり、そのコントロールの
            「各種プロパティ値を更新できない」 という意味ではありません。

    UserForm モジュール側で、コントロールを配列として操作する場合に利用します。
    Item プロパティの後にピリオドを挟んで、個々のオブジェクトのプロパティを記述できます。
        例:  object .Item( 1 ).Value

    Item プロパティ は 【既定のプロパティ】 として定義されているので、[ .Item ] を省略して
           object ( 1 ).Value
    という風に記述する事ができます。  ( Q & A )

    このプロパティは汎用の[Object 型]なので、コーディング時にプロパティ/メソッドの インテリセンス
    が機能しません。インテリセンス による一覧が必要な場合は、次の[ ItmXxx ]プロパティを使って下さい。

    インテリセンス が機能しないとは言っても、手入力でプロパティ等を記述する事はできます(ただし、
    実行時に、そのコントロールが備えていないプロパティであれば、実行時エラーになるでしょう)。

    ( Index )
      コントロール番号(Add メソッドで追加した順に1 からの連番)を指定します。

    ( 値の取得 )
      Index に指定した番号のコントロールオブジェクトを汎用の [ Object 型 ]で返します。
      番号が「1〜コントロール数」の範囲外の場合は、[ Nothing ]が返ります。
 

object .ItmLbl    ( Index ) [ = MsForms.Label ]
object .ItmCmd    ( Index ) [ = MsForms.CommandButton ]
object .ItmTxt    ( Index ) [ = MsForms.TextBox ]
object .ItmChk    ( Index ) [ = MsForms.CheckBox ]
object .ItmOpt    ( Index ) [ = MsForms.OptionButton ]
object .ItmTgl    ( Index ) [ = MsForms.ToggleButton ]
object .ItmCbo    ( Index ) [ = MsForms.ComboBox ]
object .ItmSpn    ( Index ) [ = MsForms.SpinButton ]

    [ object ]グループを構成する コントロールオブジェクトを返します。
    値の取得のみ可能です。
    (補) [値の取得のみ]とは、そのコントロールオブジェクトへの参照の事であり、そのコントロールの
            「各種プロパティ値を更新できない」 という意味ではありません。

    UserForm モジュール側で、コントロールを配列として操作する場合に利用します。
    個々のコントロールオブジェクト型ですので、コーディング時にインテリセンスが機能します。

    ( Index )
      コントロール番号(Add メソッドで追加した順番に1 からの連番)を指定します。

    ( 値の取得 )
      Index に指定した番号のコントロールオブジェクトをMsForms の [ 各コントロール型 ]で返します。
      番号が「1〜コントロール数」の範囲外の場合、および、登録されているコントロールと種別が一致しない
      場合は[ Nothing ]が返ります。

      Rgst メソッドで[AllType := True ]とした場合は、先ず、Item プロパティによって Object型で受け取り、
      TypeName関数で「コントロール型」を判定してから使うようにして下さい。
 

イベント

Private Sub object_Change ( ByVal Index As Integer )
Private Sub object_Click ( ByVal Index As Integer )
Private Sub object_DblClick ( ByVal Index As Integer, ByVal Cancel As MSForms.ReturnBoolean )
Private Sub object_KeyDown ( ByVal Index As Integer, _
                                ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer )
Private Sub object_KeyPress ( ByVal Index As Integer, ByVal KeyAscii As MSForms.ReturnInteger )
Private Sub object_KeyUp ( ByVal Index As Integer, _
                                ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer )
Private Sub object_MouseMove ( ByVal Index As Integer, ByVal Button As Integer, _
                                ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single )
Private Sub object_MouseDown ( ByVal Index As Integer, ByVal Button As Integer, _
                                ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single )
Private Sub object_MouseUp ( ByVal Index As Integer, ByVal Button As Integer, _
                                ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single )
Private Sub object_DropButtonClick ( ByVal Index As Integer )
Private Sub object_SpinDown ( ByVal Index As Integer )
Private Sub object_SpinUp ( ByVal Index As Integer )

---- 下記の 4 イベント は Ver 2.0 で追加されました ( Q & A )----
Private Sub object_OnEnter ( ByVal Index As Integer )
Private Sub object_OnExit ( ByVal Index As Integer, ByVal Cancel As MSForms.ReturnBoolean )
Private Sub object_BeforeUpdate ( ByVal Index As Integer, ByVal Cancel As MSForms.ReturnBoolean )
Private Sub object_AfterUpdate ( ByVal Index As Integer )

(補足) カスタムイベントとして、Exit イベントを定義すると、その名前が [ Exit ステートメント ] と重複するとして、
        エラーになります。その為、Exit イベントは OnExit の名前で定義しています。なお、Exit の相棒として
        Enter イベント も OnEnter の名前で定義しています。

---- 下記の イベント は Ver 3.0 で追加されました ( Q & A ) ----
Private Sub object_FakeExit ( ByVal Index As Integer )


    [ object ]に登録した各コントロールのイベントは全て、これらひとつのイベントプロシジャーで処理されます。

    各イベントの引数は、通常の MsForms コントロールのイベント引数の
           ByVal  Index  As  Integer
    を追加したパターンになっています。

    ( Index )
      イベントが発生したコントロールの番号(Add メソッドで登録した順に1から割当てられます)が返ります。
      番号は[ object ]単位で割当てられます。
      イベントが発生したコントロールにアクセスするには、[ Index ]の番号を引数にして Item または ItmXxx
      プロパティ を利用します。


    1)  利用側でイベントプロシジャーを用意しても、[ Rgst メソッド ]で指定していないイベントは
          発生しません(イベント種別コード)。
    2)  [ Rgst メソッド ] で指定していても、利用側でイベントプロシジャーを用意していなければ
          機能しません。
    3)  [ Rgst メソッド ] で指定していても、そのコントロールに本来備わっていないイベントは
          発生しません。
    4)  [ BPCA_All ] を指定して、必要なイベントのみ、利用側でプロシジャーを用意するという
          コーディングでも構いませんが、余計な負荷を掛けない為にも、必要なイベントのみ
          [ Rgstメソッド ] に指定して使って下さい。
    5)  FakeExit イベントは Frame / MultiPage 内の コントロールに対してのみ提供されます。

 
[ この場所へのリンク ]

Q & A

  (a) Item プロパティ の省略がエラーになる。
        症状 : Item プロパティ を省略して [ WeekBtn ( Index ) .BackColor ] とするとエラーになる。
                [ WeekBtn .Item ( Index ) .BackColor ] ならば エラー にならない。

  (b) Enter , Exit , BeforeUpdate , AfterUpdate の各イベントが機能しない。
        症状 : Rgst メソッド で指定したにも係わらず、Enter , Exit , BeforeUpdate , AfterUpdate の イベント
                が機能しない。

  - Answer (a) & (b) -
    原因 : clsBpca または clcBpcaCh クラスモジュール を VBE の コードウィンドウ間で コピー&ペーストした。
              (a) の場合は clsBpca 、(b) の場合は clsBpcaCh 。

    対策 : clsBpca または clsBpcaCh クラスモジュール を「モジュールの解放」で一旦削除します。
             その後、配布ファイル内の エクスポートファイル( clsBpca.cls , clsBpcaCh.cls ) をインポートします。

    解説 : Item プロパティ および 上記の4イベント のプロシージャーには、コードウィンドウ上では見えない
             Attribute ステートメント の情報が書き込まれています(エクスポートファイル上では見る事が
             できます)。 コピー&ペーストでクラスモジュールをコピーすると、その Attribute ステートメントが
             欠落してしまいます。その結果として、既定プロパティ および イベントが機能しなくなります。


  (c) Enter / Exit イベント が 発生しない場合がある。
        症状 : Frame 内の コントロール で clsBpca を利用しているが、Enter / Exit イベントが発生しない場合がある。

  - Answer (c) -
    原因 : Enter / Exit イベントの元々の挙動によるものであり、clsBpca のバグではありません。
             clsBpca を使用せず、Enter / Exit イベントを各コントロールごとに記述した場合でも同じ挙動となります。
             また、Frame だけではなく、MultiPage でも同様の症状を起こします。
             原因の詳しい解説は 『 Tips03: [Exit]を通らないで外へ? 』 をお読みください。

    対策 : Frame の場合には、ラベルコントロール を利用して 【枠】 を作ることにより、この問題を回避できます。
            (ラベルコントロールによる【枠】の作り方は上記 リンク先で説明しています)
            MultiPage の場合には、Enter/Exit を使用しない処理方法を検討する必要があります。


  (d) FakeExit イベント とは何ですか?

  - Answer (d) -
        Exit イベントは 入力内容のエラーチェック等で一般的に利用されるイベントですが、Frame / MultiPage
        コントロール内に配置された コントロール の場合に問題があります。
        Frame / MultiPage コントロール から抜け出す フォーカス移動を行なった場合、その移動元 コントロールの
        Exit イベントが発生しません(フォーカス移動のタイミングで発生しません。後で遅れて発生します)。
        この問題の詳細は [ Tips03 : Exit を通らないで外へ? ] を参照してください。

        この問題に対処する為、Frame / MultiPage コントロール から抜け出す フォーカス移動の際に、その移動元
        コントロールに対して代わりのイベント ( FakeExit ) を発生させます。具体的には、Frame_Exit / MultiPage_Exit
        / MultiPage_Change の際に移動元コントロール(Frame.ActiveControl or MultiPage.Page(x).ActiveControl )
        に対して FakeExit イベントを発生させます。尚、代替イベントですので、[ Cancel = True ] により フォーカスを
        留めるという機能はありません。また、正規の Exit イベントは、「発生しない」のではなく「遅れて発生する」 という
        事に注意してください(詳細は上記リンク記事参照)。


  (e) FakeExit イベント の利用方法は?

  - Answer (e) -
        Rgst メソッドの [ Event 引数 ] に FakeExit を指定し ( Bpca_FakeExit を加える) 、前記の FakeExit イベント
        プロシージャーの記述を用意するだけです。
        どの コントロール が、 どの Frame / MultiPage 内に収容されているのかといった事は、ユーザーサイドでは
        一切気にする必要はありません。普通に 「Add メソッドで追加 & Rgst メソッドの実行」 を行なうだけでOKです。
        収容関係は、Rgst メソッドに FakeExit を指定する事により、クラスモジュール内部で自動的に認識します。

        [ Cancel = True ] により フォーカスを留める処理が出来ません。したがって、エラーチェックに利用する場合は、
        エラーフラグを立てるのみとし、実行ボタン等でエラーの最終確認を行うようにしてください(当然、他の正規の
        Exit イベント でも [ Cancel = True ] を使わずにエラーフラグのセットのみにする)。

        Frame を多層構造にしていると、下図の赤矢印のケースでは FakeExit すらも発生させることができません。
        (理由: Frame3_Exit が発生しない為)
       
        単なる境界線としての枠ならば、以下のように、ラベルコントロールで代用できますので、ラベルを利用する
        ようにしてください。
          a) 枠の大きさのラベルコントロールを配置
          b) プロパティ設定
                キャプションなし : Caption = ""
                背景透過 : BackStyle = fmBackStyleTransparent
                枠線あり : SpecialEffect = fmSpecialEffectEtched
          c) ZOrder の設定
                ラベルコントロールを選択して、メニューの[ 書式 > 順序 > 最背面へ移動 ]



利用 方法
下記は『擬似からの脱却』での「曜日ボタン」サンプルのマクロです。これや、サンプルブックの他のコードに
倣ってコーディングして下さい。

下記の中の WeekBtn.ItmCmd(Index).xxxWeekBtn.Item(Index).xxx と書くこともできます。
更に、Item を省略して WeekBtn(Index).xxx と書くこともできます(Item は 既定のプロパティです)。


  Private WithEvents WeekBtn As clsBpca

  Private Sub UserForm_Initialize ( )
      Set WeekBtn = New clsBpca
      With WeekBtn
          .Add  cmdSun
          .Add  cmdMon
          .Add  cmdTue
          .Add  cmdWed
          .Add  cmdThu
          .Add  cmdFri
          .Add  cmdSat
          .Rgst  BPCA_Click
      End With
  End Sub

  Private Sub UserForm_Terminate ( )
      WeekBtn.Clear        ' クラス解放【必須】
      Set WeekBtn = Nothing
  End Sub

  Private Sub WeekBtn_Click ( ByVal Index As Integer )
  Dim vntWeekName As Variant
  Dim i As Integer
      vntWeekName = Array ( "", "日", "月", "火", "水", "木", "金", "土" )
      If (WeekBtn.ItmCmd ( Index ).BackColor = vbButtonFace ) Then
          For i = 1 To 7
              If ( i = Index ) Then
                  WeekBtn.ItmCmd ( i ).BackColor = &HCCFFFF    '薄黄
              Else
                  WeekBtn.ItmCmd ( i ).BackColor = vbButtonFace
              End If
          Next i
      Else
          WeekBtn.ItmCmd ( Index ).BackColor = vbButtonFace
      End If
      MsgBox vntWeekName ( Index ) & "曜日ボタンがクリックされました(" & Index & ")"
  End Sub

   上記のように、Add メソッドで追加する前に[ New キーワード ]付きの Set ステートメントで、 object に
    対して[ clsBpca ]クラスを設定する必要があります。

    各イベントプロシジャーの扱いは、通常のコントロールの場合と同じです。引数の先頭に加えられている
    [ Index ]を使って、[ Item ] もしくは[ ItmXxx ]プロパティにより個々のコントロールオブジェクトを操作
    して下さい。その他は、実際の利用例として『 Bpca_Class.xls 』のサンプルを参考にして下さい。
 


[ この場所へのリンク ]
====================================================
    イベント発生指示コード ( modBpcaConst )
====================================================
  [ clsBpca ] クラスを使用するには、[イベント種別] 定数を定義した[ modBpcaConst ] を
      一緒にインポートする必要があります。定義されている内容は下記になります。
  Rgst メソッド の Event 引数は Ver 2.0 から 列挙型 [ As BPCA_Event ] に変わりました。

Public Const BPCA2_Change         As Long = 1            '[ 1]bit (ON)
Public Const BPCA2_Click          As Long = 2            '[ 2]
Public Const BPCA2_DblClick       As Long = 4            '[ 3]
Public Const BPCA2_KeyDown        As Long = 8            '[ 4]
Public Const BPCA2_KeyPress       As Long = 16           '[ 5]
Public Const BPCA2_KeyUp          As Long = 32           '[ 6]
Public Const BPCA2_MouseMove      As Long = 64           '[ 7]
Public Const BPCA2_MouseDown      As Long = 128          '[ 8]
Public Const BPCA2_MouseUp        As Long = 256          '[ 9]
Public Const BPCA2_DropBtnClick   As Long = 512          '[10]
Public Const BPCA2_SpinDown       As Long = 1024         '[11]
Public Const BPCA2_SpinUp         As Long = 2048         '[12]
Public Const BPCA2_Enter          As Long = 4096         '[13]
Public Const BPCA2_Exit           As Long = 8192         '[14]
Public Const BPCA2_BeforeUpdate   As Long = 16384        '[15]
Public Const BPCA2_AfterUpdate    As Long = 32768        '[16]
Public Const BPCA2_FakeExit       As Long = 65536        '[17]

Public Const BPCA2_All As Long = &HFFFF&    '(65535,16bit all-ON, FakeExitを除く)

Public Const BPCA2_KeyDU As Long = BPCA2_KeyDown + BPCA2_KeyUp
Public Const BPCA2_KeyDPU As Long = BPCA2_KeyDown + BPCA2_KeyPress + BPCA2_KeyUp
Public Const BPCA2_MouseDU As Long = BPCA2_MouseDown + BPCA2_MouseUp
Public Const BPCA2_MouseMDU As Long = BPCA2_MouseMove + BPCA2_MouseDown + BPCA2_MouseUp
Public Const BPCA2_SpinDU As Long = BPCA2_SpinDown + BPCA2_SpinUp
Public Const BPCA2_EnterExit As Long = BPCA2_Enter + BPCA2_Exit
Public Const BPCA2_BAUpdate As Long = BPCA2_BeforeUpdate + BPCA2_AfterUpdate

Public Const BPCA2_Except_MouseM As Long = BPCA2_All - BPCA2_MouseMove


'-- Enumeration list of Event type constants --
Public Enum BPCA_Event
    BPCA_Change           = BPCA2_Change
    BPCA_Click            = BPCA2_Click
    BPCA_DblClick         = BPCA2_DblClick
    BPCA_KeyDown          = BPCA2_KeyDown
    BPCA_KeyPress         = BPCA2_KeyPress
    BPCA_KeyUp            = BPCA2_KeyUp
    BPCA_MouseMove        = BPCA2_MouseMove
    BPCA_MouseDown        = BPCA2_MouseDown
    BPCA_MouseUp          = BPCA2_MouseUp
    BPCA_DropBtnClick     = BPCA2_DropBtnClick
    BPCA_SpinDown         = BPCA2_SpinDown
    BPCA_SpinUp           = BPCA2_SpinUp
    BPCA_Enter            = BPCA2_Enter
    BPCA_Exit             = BPCA2_Exit
    BPCA_BeforeUpdate     = BPCA2_BeforeUpdate
    BPCA_AfterUpdate      = BPCA2_AfterUpdate
    BPCA_FakeExit         = BPCA2_FakeExit

    BPCA_All              = BPCA2_All

    BPCA_KeyDU            = BPCA2_KeyDU
    BPCA_KeyDPU           = BPCA2_KeyDPU
    BPCA_MouseDU          = BPCA2_MouseDU
    BPCA_MouseMDU         = BPCA2_MouseMDU
    BPCA_SpinDU           = BPCA2_SpinDU
    BPCA_EnterExit        = BPCA2_EnterExit
    BPCA_BAUpdate         = BPCA2_BAUpdate
    BPCA_Except_MouseM    = BPCA2_Except_MouseM
End Enum



[ この場所へのリンク英語ページ ]

(2014/8/11  追記)
〜 API : ConnectToConnectionPoint によるイベント処理の構築 〜
    この記事の執筆にあたっては 「Abyss さん , yayadon さん」 に協力して貰いました。有難うございました。
    参考 : moug / 給湯室 / VBAクラス研究室 (5) [ アーカイブ ]
             スレッド中程の [角田 14/07/29 12:47] 〜 [角田 14/08/11 19:51]

通常のイベント処理のプログラミングとの比較をしつつ説明します。

〜 VBAにおける通常のイベント処理のプログラミング 〜
    (a) イベントソース(UserFormに配置されたコントロール、WithEvents 付きのオブジェク
        ト変数)に対するイベントハンドラ(Enter , Click 等のイベントプロシジャー)を、下記
        の命名ルールに基づく名前のプロシジャーとして用意します。

        Sub  イベントソース名 + アンダースコア + イベント名 ( 引数1, 引数2, …… )

                (i) UserForm上のコマンドボタン ( CommandButton1 ) のClick イベントの場合
                         Private Sub CommandButton1_Click ( )
                (ii) UserForm上のテキストボックス ( TextBox1 )の Exit イベントの場合
                        Private Sub TextBox1_Exit ( ByVal Cancel As MSForms.ReturnBoolean )
                (iii) clsTimer というクラスの TimeOut という名前のイベントの場合
                        Private WithEvents TimerCtrl As clsTimer
                        Private Sub TimerCtrl_TimeOut ( 引数 )

    (b) そのプロシジャーの中に、そのイベントで実行する処理を記述する。

    (c) 「イベントの発生」 と 「イベントハンドラの実行」の間を繋ぐのはVBAシステムが
        担ってくれます(VBAは、イベントハンドラの命名ルールを基にして両者を紐付け
        します。)

    (d) ひとつのクラスモジュール(または、UserFormモジュール)にイベントソースとなる
        コントロールオブジェクトは幾つあっても構わない(イベントハンドラが「イベントソー
        ス名+イベント名」なので互いに区別できる)。
 -- 通常のイベントコーディング --
 
 クラスモジュール内に2つのイベントソースがあっても、名前で区別できるので
 各々に対応したイベントハンドラを記述できる

 
 Private WithEvents TBox1 As MsForms.TextBox
 Private WithEvents TBox2 As MsForms.TextBox
 
 Private Sub TBox1_Enter ( )
 End Sub
 
 Private Sub TBox2_Enter ( )
 End Sub




〜 API : ConnectToConnectionPoint によるイベント処理のプログラミング 〜
    先ずは、ConnectToConnectionPoint によるイベントコーディングを見て貰いましょう。
    (ConnectToConnectionPoint の使い方を見る為だけですので、単純にしています)

    UserForm に テキストボックスを4個(TextBox1 〜 TextBox4)用意します。クラスモジュール(clsC2CP)内で、
    Enter, Exit, BeforeUpdate, AfterUpdate イベントを受けてDebug.Print によりイミディエイトウィンドウに実行
    状況を出力します。 単純化する為に、イベント通知をUserFormに上げる処理は組み込んでいません。 


      【 UserForm1 モジュール 】
 ' UserFormにイベントを通知する処理を組み込んでいない(WithEvents は不要)ので、
 ' クラスオブジェクトを配列で定義しています。

 Private aryTextBox( 1 To 4 ) As clsC2CP
 
 Private Sub UserForm_Initialize( )
 Dim i As Integer
     For i = 1 To 4
         Set aryTextBox( i ) = New clsC2CP
         aryTextBox( i ).Item = Me.Controls( "TextBox" & i )
         aryTextBox( i ).Index = i
     Next i
 End Sub
 
 Private Sub UserForm_Terminate( )
 Dim i As Integer
     For i = 1 To 4
         aryTextBox( i ).Clear
     Next i
 
     Erase aryTextBox
 End Sub

 ' ConnectToConnectionPoint の動作確認をするだけなので、
 ' UserForm にイベントを通知する処理は省いています。
 ' (Debug.Print はクラスモジュール内で行なっています)



      【 clsC2CP  クラスモジュール 】
        ConnectToConnectionPoint の動作例ですので、 それを略してクラスモジュールの名前を C2CP としています)
        赤字は エクスポートファイル上で書き込む(コードウィンドウ上には表示されない)。
 ' API 定義 [ ConnectToConnectionPoint ]
 Private Type GUID
     Data1 As Long
     Data2 As Integer
     Data3 As Integer
     Data4( 0 To 7 ) As Byte
 End Type
 
 #If VBA7 And Win64 Then
     Private Declare PtrSafe Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" _
             (ByVal punk As stdole.IUnknown, ByRef riidEvent As GUID, _
             ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, _
             ByRef pdwCookie As Long, Optional ByVal ppcpOut As LongPtr) As Long
 #Else
    Private Declare Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" _
             (ByVal punk As stdole.IUnknown, ByRef riidEvent As GUID, _
             ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, _
             ByRef pdwCookie As Long, Optional ByVal ppcpOut As Long) As Long
 #End If
 
 Private Cookie As Long      'pdwCookie of ConnectToConnectionPoint
 '-----------------------------------------------------------
 
 Private MyCtrl As Object      'Event Source
 Private MyIndex As Integer
 
 Private Sub ConnectEvent( ByVal Connect As Boolean )
 Dim IID_IDispatch As GUID
     ' GUID {00020400-0000-0000-C000000000000046}
     With IID_IDispatch
         .Data1 = &H20400
         .Data4( 0 ) = &HC0
         .Data4( 7 ) = &H46
     End With
     ConnectToConnectionPoint _
             Me, IID_IDispatch, Connect, MyCtrl, Cookie, 0&
 End Sub
 
 Public Property Let Index( NewIndex As Integer )
     MyIndex = NewIndex
 End Property
 
 Public Property Let Item( NewCtrl As Object )
     Set MyCtrl = NewCtrl
     Call ConnectEvent( True )      'Start Connect Event
 End Property
 
 Public Sub Clear( )
     If ( Cookie <> 0 ) Then
         Call ConnectEvent( False )      'Finish Connect Event
     End If
     Set MyCtrl = Nothing
 End Sub
 
 '=== Event Handler ===
 ' イベントソースから呼び出されるプロシジャーなので 【 Public 】にします。
 
 ' List of VB_UserMemId
 '     Enter               &H80018202 = -2147384830
 '     Exit                 &H80018203 = -2147384829
 '     BeforeUpdate    &H80018201 = -2147384831
 '     AfterUpdate      &H80018200 = -2147384832

 
 Public Sub HookEnter( )
 Attribute HookEnter.VB_UserMemId = -2147384830
     Debug.Print MyCtrl.Name & "(" & MyIndex & ") [Enter] Value=" & MyCtrl.Value
 End Sub
 
 Public Sub HookExit( ByVal Cancel As MSForms.ReturnBoolean )
 Attribute HookExit.VB_UserMemId = -2147384829
     Debug.Print MyCtrl.Name & "(" & MyIndex & ") [Exit] Value=" & MyCtrl.Value
 End Sub
 
 Public Sub HookBeforeUpdate( ByVal Cancel As MSForms.ReturnBoolean )
 Attribute HookBeforeUpdate.VB_UserMemId = -2147384831
     Debug.Print MyCtrl.Name & "(" & MyIndex & ") [BeforeUpdate] Value=" & MyCtrl.Value
 End Sub
 
 Public Sub HookAfterUpdate( )
 Attribute HookAfterUpdate.VB_UserMemId = -2147384832
     Debug.Print MyCtrl.Name & "(" & MyIndex & ") [AfterUpdate] Value=" & MyCtrl.Value
 End Sub
 

      【 実行結果 : イミディエートウィンドウ 】

 TextBox1(1) [Enter] Value=
 TextBox1(1) [BeforeUpdate] Value=1
 TextBox1(1) [AfterUpdate] Value=1
 TextBox1(1) [Exit] Value=1
 TextBox2(2) [Enter] Value=
 TextBox2(2) [BeforeUpdate] Value=aa
 TextBox2(2) [AfterUpdate] Value=aa
 TextBox2(2) [Exit] Value=aa
 TextBox3(3) [Enter] Value=
 TextBox3(3) [BeforeUpdate] Value=3456
 TextBox3(3) [AfterUpdate] Value=3456
 TextBox3(3) [Exit] Value=3456
 TextBox4(4) [Enter] Value=
 TextBox4(4) [BeforeUpdate] Value=qwer
 TextBox4(4) [AfterUpdate] Value=qwer
 TextBox4(4) [Exit] Value=qwer
 TextBox1(1) [Enter] Value=1
 TextBox1(1) [Exit] Value=1


    (A) イベントハンドラ(イベントプロシジャー)の名前は任意です。イベントソースの名前を
        付する必要はありません。
          接頭辞としてイベントソースの名前を付したとしても、その事自体には何の意味もありません。
              それは、あくまでも、プログラムの可読性の向上に寄与するだけです。


    (B) そのプロシジャーの中に、そのイベントで実行する処理を記述するという点は同じです。

    (C) 「イベントの発生」 と 「イベントハンドラの実行」の間を繋ぐ役割りを
             API の ConnectToConnectionPointAttribute ステートメント
        が担います。

        (i)  ConnectToConnectionPoint は、イベントソースが発信するイベントメッセージを受信
            するオブジェクトをイベントソースに知らせ(、登録し)ます

            ConnectToConnectionPoint _
                      Me, IID_IDispatch, Connect, MyCtrl, Cookie, 0&
                      I                             I
                      +-----------------------------+
                                    MyCtrl (イベントソース) のイベントメッセージを
                                    Me (例では clsC2CPクラスオブジェクト) で受け取ります、という宣言。


                イベントメッセージを受信するオブジェクトは、Sink (シンク)オブジェクト または イベントシンク 等と呼ば
                    れます(次々と流れ出る水(イベントメッセージ)を受け止める流し台(イベントシンク)という意味です)。
                実際には、
                        ・ イベントメッセージを受け取り
                        ・ そのメッセージに対応するイベントハンドラーを探し出して
                        ・ 見つかったイベントハンドラーに処理を引き継ぐ
                    という役目を Invoke メソッド というものが担っています(VBAユーザーからは一切見えない部分です。
                    マクロ にもその姿はありません。詳細は MSDNライブラリ 等を閲覧して下さい)
                VBAのObject 型(および クラスモジュール) には、この Invoke メソッド の機能が備わって いますので、
                    ConnectToConnectionPoint の引数として指定する事ができます。


        (ii) Attribute ステートメントは、イベントの種別を表す値をプロシジャーに付与します(上記
            サンプルマクロ参照)。その付与によって、そのプロシジャーは指定した種別のイベントを
            受信するイベントハンドラー(イベントプロシジャー)として宣言されます。
              Attribute ステートメントは、イベントハンドラ (イベントプロシジャー) の Sub ステートメントの直下に記述します。
                  (コードウィンドウからは書き込めません。エクスポートファ イル上で書き込み、その後インポートします)。


       (iii) イベントソースが発信する各種のイベントメッセージにも、各々そのイベントを表す値が含
            まれています。上記の Invoke メソッドが、メッセージのイベント種別と同じ値を付与されて
            いるプロシジャーを探し出して(この為にAttributeステートメントによってイベント種別を設
            定しています)、そのプロシジャーに処理を引き継ぎます。

        ConnectToConnectionPoint を使ったイベント処理では、このようにしてイベントソース と
        イベントハンドラーの間を繋いでいます。

    (D) Attribute ステートメントには「受信するイベントの種別」しか定義されていないので、
        ConnectToConnectionPoint で扱えるイベントソースとなるコントロールオブジェクトは、
        ひとつのクラスモジュール(または、UserFormモジュール)に只ひとつです。
 -- ConnectToConnectionPointによるイベントコーディング --
 
 クラスモジュール内に2つのイベントソースがあると……

 
 Private TBox3 As Object
 Private Cookie3 As Long
 
 Private TBox4 As Object
 Private Cookie4 As Long
 
 ConnectToConnectionPoint _
         Me, IID_IDispatch, Connect, TBox3, Cookie3, 0&
 
 ConnectToConnectionPoint _
         Me, IID_IDispatch, Connect, TBox4, Cookie4, 0&
 
     ↑ ここまでは個々で記述が出来たとしても……
 
     ↓ Attribute にはイベントの種別しか指定しないので
         イベントハンドラを2つ記述しても、どちらがどちらに対応するのか区別できない。
         (TBox3 のイベントハンドラーは HookEnterA ? それとも HookEnterB ? )
 
 Public Sub HookEnterA ( )
 Attribute HookEnterA.VB_UserMemId = -2147384830
 End Sub
 
 Public Sub HookEnterB ( )
 Attribute HookEnterB.VB_UserMemId = -2147384830
 End Sub
 
        このように、ConnectToConnectionPointによるイベントコーディングでは
         『イベントソースはひとつだけ』となります。
        そもそも、ConnectToConnectionPoint を使用する場合は、最初から、
              イベントソースひとつに、クラスひとつ
        という設計にします。


  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    実を言えば、clsBpca でサポートしているイベントは、ConnenctToConnectionPoint だけで全て
    対応できます(Attributeステートメントに、Change, Click 等に対応するイベント種別の値を指定
    すれば良いだけです)。
    しかし、一般のVBAユーザーも clsBpca クラスのマクロコードを閲覧するという前提に立てば、
    全てがConnectToConnectionPoint で作られているのは好ましい状況ではないと考えます。
    (ここで、このように解説していても、一般のVBAユーザーには理解は難しいでしょう)

    その為、clsBpca では、敢えて、通常のイベントプログラミングが出来るイベント(Enter, Exit,
    BeforeUpdate, AfterUpdate 以外)については、通常のイベントプログラミングでコーディング
    しています。




  ====================================================================
  『擬似からの脱却』 汎用クラスモジュール [ clsBpca ] リファレンス (Ver 2.0)
  ====================================================================

[ 前頁 , 次頁 , §1 , §2 , §3 , §4 , §5 , §6 , §7 , §8 , §9 , §10 , §11 , §12 ]
[ 汎用クラス , トグルラベル クラス , Focus クラス , クラス アドイン , カレンダークラス ] [ 質問はこちら]


[ Home へ戻る ]

ロゴ(ゴールド)   ロゴ(ゴールド)

角田 桂一 Mail:addinbox@h4.dion.ne.jp CopyRight(C) 2004 Allrights Reserved.