【 擬似からの脱却 】 [ Breakthrough in the Pseudo-Control Arrays ]
[ clsBpca の 軌跡 ] [ 前頁 , 次頁 , §1 , §2 , §3 , §4 , §5 , §6 , §7 , §8 , §9 , §10 , §11 , §12 ]
[ 汎用クラス , トグルラベル クラス , Focus クラス , クラス アドイン , カレンダークラス ] [ 質問はメールへ ]
このクラスはx64 でも動作します 。
Ver 2.0 より、Excel97 はサポート対象外 となりました( Excel97 でも利用可能な Ver 1.4 は こちら です)。
Ver 2.0 以降、Windows-API を使用している為、Mac-PC では動作しません ( Mac-PC でも利用可能な 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 ]
※ Windows-API を使用している為、Mac-PC では利用できなくなりました ( Mac-PC では Ver 1.4 を使用してください )
このクラスの使用方法は下記サンプルブックの「利用例フォーム」と、後述のリファレンスガイドを
参考にして下さい。
本来の「配列化」するという目的の他に、『動的に追加したコントロール』のイベント処理 にも
利用できます。
【 clsBpca コンセプト解説図 】
[ clsBpca ] クラス定義 および サンプル ブック [ Bpca_Class_V41.zip ] (211 KB)
(注) 「一般的にダウンロードされておらず 〜」 というメッセージが出たら
(配布ファイル) Bpca_Class_V41.zip
+-- Bpca_Class_V41.xls (クラス定義 および サンプルブック)
+-- ExportV41 (エクスポート ファイル)
+-- clsBpca.cls
+-- clsBpcaCh.cls
+-- modBpcaConst.bas
+-- mdlHoliday.bas ( カレンダーフォーム用 祝日モジュール , [東京五輪 祝日移動(2021年)] 対応済 )
+-- 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 イベント処理で実行時エラーになる不具合を修正しました。
( 2018/4/29 Ver 3.2 ) a) クラスモジュールの機能変更はありません。
カレンダーフォーム用の祝日モジュールを[天皇誕生日の変更] 対応で差し替えただけです。
( 2018/6/26 Ver 3.3 ) a) クラスモジュールの機能変更はありません。
カレンダーフォーム用の祝日モジュールを[東京五輪 祝日移動(2020年) 他] 対応で差し替えただけです。
( 2018/12/10 Ver 3.4 ) a) クラスモジュールの機能変更はありません。
カレンダーフォーム用の祝日モジュールを[即位関連休日] 対応で差し替えただけです。
( 2020/9/1 Ver 4.0 ) a) Ver 3.0〜3.4 にて、ワークシート上のコントロールを指定した場合に実行時エラーとなる不具合を修正しました。
( 2020/11/28 Ver 4.1 ) a) クラスモジュールの機能変更はありません。
カレンダーフォーム用の祝日モジュールを[東京五輪 祝日移動(2021年)] 対応で差し替えただけです。
このクラスモジュールを利用する方法は 2つ あります。
(1) 各自のワークブック内にクラスモジュールを取り込んで利用する。
上記の配布ファイル ( Bpca_Class_V41.zip ) にクラスモジュールのエクスポートファイル
が用意してあります。そのエクスポートファイルを各自のワークブックに【インポート】で
取り込んでください。
(注意事項)
VBE のコードウィンドウ内で、Bpca_Class_V41.xls から 各自のワークブックへ
コピー&ペーストによりクラスモジュールをコピーしないでください。
コピー&ペーストでクラスモジュールを取り込むと、以下の機能が働きません。( 後述 )
a) Item プロパティを『既定のプロパティ』にする。
b) Enter, Exit, BeforeUpdate, AfterUpdate の4つのイベント。
(2) クラスモジュールのアドインブックを利用する。
汎用クラス(clsBpca) と他に幾つかのクラスモジュールを一緒にして アドインブック
としても提供しています。
この場合は、各自のワークブックにクラスモジュールを取り込む必要はありません。
VBEの「ツール/参照設定」でアドインブック( ktClassAddin.xla ) を参照設定する
だけで利用可能になります。各自のワークブック内に取り込む場合とで、利用上の
違いはありません(クラスインスタンスの生成方法のみ異なります)。
クラスアドイン Ver 3.60 (clsBpca Ver 4.1 を収録)
[ 2005/ 3/ 8 Ver 1.00 ]
[ 2014/ 8/11 Ver 3.00 ]
[ 2016/10/11 Ver 3.10 ]
[ 2018/ 4/29 Ver 3.20 ]
[ 2018/ 6/26 Ver 3.30 ]
[ 2018/12/10 Ver 3.40 ]
[ 2020/ 9/ 1 Ver 3.50 ]
[ 2020/11/28 Ver 3.60 最新版で再構築 ]
=======================================================================
「擬似からの脱却」汎用クラス [ clsBpca ] イベント/メソッド/プロパティ一覧
=======================================================================
a) このクラスを利用するには下記3モジュールをインポートで各自のワークブックに
取り込む必要があります(クラスアドインを利用する場合は不要です)。
・ clsBpca & clsBpcaCh (クラスモジュール)
・ modBpcaConst (「イベント種別」定数を定義した標準モジュール)定義内容はこちら
b) [ clsBpcaCh ] クラスは、[ clsBpca ]クラスの下位クラスです(ユーザーサイドで
直接このクラスを使うことはありません)。
c) このクラスは Excel2000 以上で利用できます( x64 でも動作します)。
※ Mac-PC では利用できません。Mac-PC では Ver 1.4 を利用してください。
下記で[ object ]の部分には、clsBpca クラスを定義したオブジェクト変数を記述します。
【 定 義 】 UserForm モジュールの宣言セクション(モジュールの先頭)で、下記のように定義します。
Private WithEvents object As clsBpca
1UserForm内/1ブック内で、[ object ] を変えて複数定義しても構いません。
( object )
任意の名前を指定して下さい。
[ Initialize ] イベントプロシジャーにて下記のようにしてクラスのインスタンスを作成します。
Set object = New clsBpca
※ ワークシート上の コントロール を指定する場合は ThisWorkbook モジュール に記述します(利用例)。
※ アドイン化したクラスを利用している場合は、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 コントロール オブジェクト )
コントロール配列化する、コントロールオブジェクトを指定します。
※ ワークシート上の コントロール を指定する場合は下記のスタイルで記述します(利用例)。
[ ワークシート.OLEObjects ( コントロール名 ).Object ]
※ ワークシート上の コントロール を指定する場合は
ThisWorkbook モジュールの [ Workbook_Open ] イベントに記述します(利用例)。
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 )します。
※ ワークシート上の コントロール を指定する場合は
ThisWorkbook モジュールの [ Workbook_Open ] イベントに記述します(利用例)。
object .Clear
クラスの解放などの終了処理です。
UserForm の[Terminate ] イベントで必ず実行して下さい。
※ ワークシート上の コントロール を指定する場合は
ThisWorkbook モジュールの [ Workbook_BeforeClose ] イベントに記述します(利用例)。
下記メソッドは、[ 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 の設定
ラベルコントロールを選択して、メニューの[ 書式 > 順序 > 最背面へ移動 ]
(f) ワークシート上の コントロールに対しても利用できますか?
- Answer (f) -
ワークシート上の コントロール に対しても利用可能です。利用例 を参考にして下さい
但し、ワークシート上の コントロール に対しては Enter / Exit イベントは機能しません。
【 利用 方法 】 その他、各種利用例の マクロコード を参考にして下さい。
下記は『擬似からの脱却』での「曜日ボタン」サンプルのマクロです。これや、サンプルブックの他のコードに
倣ってコーディングして下さい。
下記の中の WeekBtn.ItmCmd(Index).xxx は WeekBtn.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 i As Integer
Dim vntWeekName As Variant
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
[ この場所へのリンク ( Link to here ) , 英語ページ ( Eng. site ) , 擬似からの脱却 TopPage ]
(2014/8/11 追記)
この記事の執筆にあたっては 「Abyss さん , yayadon さん」 に協力して貰いました。有難うございました。
〜 API : ConnectToConnectionPoint によるイベント処理の構築 〜
参考 : moug / 給湯室 / VBAクラス研究室 (5) [ アーカイブ ]
スレッド中程の [角田 14/07/29 12:47] 〜 [角田 14/08/11 19:51]
VBAのクラスモジュールでは、TextBox/CommandButton 等の 「MsForms コントロール」
に対する Enter/Exit/BeforeUpdate/AfterUpdate イベントを定義できません。
何とも残念で不便な VBA仕様ですが、API ( ConnectToConnectionPoint ) を利用する事によって、
Exit イベント等の発生をキャッチすることが出来ます。ただし、通常のイベントプロシジャーとは異なり、
仕組みが多少難解ですので、通常のイベント処理のプログラミングとの比較をしつつ説明します。
〜 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
Call 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 の ConnectToConnectionPoint と Attribute ステートメント
が担います。
(i) ConnectToConnectionPoint は、『イベントソースが発信するイベントメッセージ』が、
「どのオブジェクトで受信される」のかを、イベントソースに知らせ(、登録し)ます。
Call 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によるイベントコーディングでは
-- ConnectToConnectionPointによるイベントコーディング --
クラスモジュール内に2つのイベントソースがあると……
Private TBox3 As Object
Private Cookie3 As Long
Private TBox4 As Object
Private Cookie4 As Long
Call ConnectToConnectionPoint _
(Me, IID_IDispatch, Connect, TBox3, Cookie3, 0&)
Call 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 を使用する場合は、最初から、
イベントソースひとつに、クラスひとつ
という設計にします。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
実を言えば、clsBpca でサポートしているイベントは、ConnenctToConnectionPoint だけで全て
対応できます(Attributeステートメントに、Change, Click 等に対応するイベント種別の値を指定
すれば良いだけです)。
しかし、一般のVBAユーザーも clsBpca クラスのマクロコードを閲覧するという前提に立てば、
全てがConnectToConnectionPoint で作られているのは好ましい状況ではないと考えます。
(ここで、このように解説していても、一般のVBAユーザーには理解は難しいでしょう)
その為、clsBpca では、敢えて、通常のイベントプログラミングが出来るイベント(Enter, Exit,
BeforeUpdate, AfterUpdate 以外)については、通常のイベントプログラミングでコーディング
しています。
[ 前頁 , 次頁 , §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. |