ロゴ(青) 擬似からの脱却 ロゴ(緑)

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

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

  ====================================================================
  §8  擬似から先ず1歩踏み出す
  ====================================================================

2005/3/11 解説文・クラスソースにおいて、オブジェクト定義時での[New]指定 を止めて、Initialize 時に
                インスタンス生成(Set ステートメント)するように修正しました。
2010/3/12 読者の方から頂いた質問とその回答を記載しまいした。


(前節より‥‥‥)
      擬似コントロール配列を一歩進めて、VB並みのイベントコーディングを
      実現する方法について考えてみましょう


  擬似コントロール配列手法では
      ・ コントロールオブジェクトへのアクセスは
               コレクションや[ Controls ]を通して、見た目上「配列」処理
      ・ クリックイベント処理のコードは
              [ clsCmdWeek ]クラスモジュール内の1箇所に記述があるだけ(一元化)
という風にしていますね。

  5節に掲載したコードを見ると判りますが、UserForm内で配列処理する為の
      ・コレクションもしくはオブジェクト変数への設定
と、イベント処理の為の
      ・クラスオブジェクト配列への設定
という2つのループが存在しています。勿論、変数定義も「コレクションもしくはオブジェクト変数」
と「クラスオブジェクト変数」の2つが配列として定義されています。必要であるのは判っています
が、どうも、ダブッているようで無駄なようにも感じます。ひとつに纏められれば申し分ないですね。

  イベントについては、
      ・クラスモジュール上にあるコーディングがひとつだけ
ですが、5節で解説したように、クラスで定義されたものは実行時に実体として、
      ・個々のコントロールに対応してひとつずつ
存在している訳です。したがって、実行時について考えれば、
      UserFormモジュールに個々のコントロールのイベントコーディングをズラズラ〜と書いていた
のと同じ状態になっている訳ですね。

  5節で掲載したクラスの中には「何番目」といった情報を[ Index ]で持たせてありますが、
      クラスのイベントルーチン内から、[ Index ]を引数に持たせて
      UserFormモジュール内のサブルーチンを呼び出す(呼び返す, Callback

という処理をしてやれば、
      UserForm側でも「今、何番のコントロールで、Clickイベントが発生した」
という認識が可能です。そうすると、イベントコーディングの本体を再びUserFormモジュールに
記述する事が可能となります。

  クラス内からUserFormモジュールのサブルーチンを呼び出す方法は前節で解説しましたので、
UserForm参照を[Object型]で受け取る Caller プロパティを用意してコードを書いてみます。
  '====== clsCmdWeek クラスモジュール =====
  Private WithEvents MyCtrl As MsForms.CommandButton
  Private MyIndex As Integer
  Private MyCaller As Object    'サブルーチン Call の為に UserForm変数をObject 型にする必要あり

  Public Property Let Item ( NewCtrl As MsForms.CommandButton )
      Set MyCtrl = NewCtrl
  End Property

  Public Property Let Index ( NewIndex As Integer )
      MyIndex = NewIndex
  End Property

  Public Property Let Caller ( NewCaller As Object )
      Set MyCaller = NewCaller
  End Property

  Private Sub MyCtrl_Click ( )
      Call MyCaller.RaiseClick ( MyIndex )    'MyCaller は、Object型のUserForm参照
  End Sub

  '===== UserForm1 モジュール =====
  Private colWeekBtn As Collection
  Private cmdWeekBtn ( 1 To 7 ) As clsCmdWeek

  Private Sub UserForm_Initialize ( )
  Dim i As Integer
      Set colWeekBtn = New Collection    ' インスタンスの生成
      With colWeekBtn
          .Add cmdSun
              :
          .Add cmdSat
      End With
      For i = 1 To 7
          Set cmdWeekBtn ( i ) = New clsCmdWeek    ' インスタンスの生成
          With cmdWeekBtn ( i )
              .Item = colWeekBtn ( i )
              .Index = i
              .Caller = Me
          End With
      Next i
  End Sub

  Public Sub RaiseClick ( ByVal Index As Integer )
  Dim vntWeekName As Variant
      vntWeekName = Array ( "", "日", "月", "火", "水", "木", "金", "土" )
      MsgBox vntWeekName ( Index ) & _
                          "曜日ボタンがクリックされました(" & Index & ")"
      If ( colWeekBtn( Index ).BackColor = vbButtonFace ) Then
          colWeekBtn( Index ).BackColor = vbRed
      Else
          colWeekBtn( Index ).BackColor = vbButtonFace
      End If
  End Sub
 
どうでしょう。イベントコーディングがVB似になって来ましたね。

  イベント取得のみをクラスへ追い出して一元化し、
        イベント発生と同時にUserForm側へキックバックして貰う
というイメージです。

  ここで、クラスに下記のようなコントロールオブジェクトの取得プロパティを用意すると、UserForm
側でコレクションを使わなくても済むようになります。  (注-1)
  '====== clsCmdWeek クラスモジュール =====
  Public Property Get Item ( ) As MsForms.CommandButton
      Set Item = MyCtrl
  End Property

  '===== UserForm1 モジュール =====
  Private cmdWeekBtn ( 1 To 7 ) As clsCmdWeek

  Private Sub UserForm_Initialize ( )
  Dim i As Integer
      For i = 1 To 7
          Set cmdWeekBtn ( i ) = New clsCmdWeek    ' インスタンスの生成
          With cmdWeekBtn ( i )
              .Item = Me.Controls ( "cmdWeek" & i )
              .Index = i
              .Caller = Me
          End With
      Next i
  End Sub

  Public Sub RaiseClick ( ByVal Index As Integer )
  Dim vntWeekName As Variant
      vntWeekName = Array ( "", "日", "月", "火", "水", "木", "金", "土" )
      MsgBox vntWeekName ( Index ) & _
                      "曜日ボタンがクリックされました(" & Index & ")"
      If ( cmdWeekBtn( Index ).Item.BackColor = vbButtonFace ) Then
          cmdWeekBtn( Index ).Item.BackColor = vbRed
      Else
          cmdWeekBtn( Index ).Item.BackColor = vbButtonFace
      End If
  End Sub
 
 これで、この節の最初に「このままでは無駄なような、と感じる」と書いた
          ・コレクションもしくはオブジェクト変数への設定ループ
          ・クラスオブジェクト配列への設定ループ
          ・変数定義も「コレクションもしくはオブジェクト変数」と
            「クラスオブジェクト変数」の2つが配列として定義
      どうも、ダブッているようで無駄なような‥‥‥
      実行時について考えれば、
          ・個々のコントロールのイベントコーディングをズラズラ〜と書いていた
      のと同じ状態

の2つが解消されました。

  トグルラベルクラス[2版]97用クラスモジュールは、この手法を用いています。
        尚、トグルラベルクラス[2版]の開発時点では、UserForm参照を[Object型]にすれば、サブルーチン呼び出しが
        可能になるという事が判っていませんでしたので、[Me.Name]のUserFormを引き継いで、分岐処理を行なって
        いました(利用にあたって、クラスモジュールの手直しが必須でした)。


 (注-1)  〜 読者の方から質問を頂きました 〜  ( 2010/3/12 追記 )

 『 クラスに下記のような_コントロールオブジェクトの取得プロパティ を用意
   すると、UserForm側でコレクションを使わなくても済むようになります。』
 とありますが、 「コレクションを使わなくても済む」のは、
      ・ コントロールオブジェクトの取得プロパティを用意したから
 ではなく、
      ・ オブジェクト名を規則的なものに変更したから
 ではないのでしょうか?


 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
 前者の方では、UserFormモジュール内の全てのプロシジャーからコントロールオブジェクトの
 配列(Collection)を使えるように、モジュールレベル変数として定義してあります。

        Private colWeekBtn As Collection      '←モジュールレベル変数
        Private cmdWeekBtn ( 1 To 7 ) As clsCmdWeek
        Private Sub UserForm_Initialize ( )
        Dim i As Integer
          Set colWeekBtn = New Collection
          With colWeekBtn
              .Add cmdSun
              (中略)
        Public Sub RaiseClick ( ByVal Index As Integer )
          colWeekBtn( Index ).BackColor = vbRed
 
 この colWeekBtn は、UserForm1が実行中は、ず〜っと活きていますし、インスタンス生成時の
 コーディング手間(ステップ数)を省く為という目的もありますが、『コントロールオブジェクトの配
 列的操作』というのがメインの使い道です。

 後者の方の例でも、[cmdSun,cmdMon … cmdSat]で作った場合にはやはり、colWeekBtn を使い
 ますが、今度は【プロシジャー内変数】で済ませる事ができます。

        Private cmdWeekBtn ( 1 To 7 ) As clsCmdWeek
        Private Sub UserForm_Initialize ( )
        Dim colWeekBtn As Collection      '← プロシジャー内変数
        Dim i As Integer
          Set colWeekBtn = New Collection
          With colWeekBtn
              .Add cmdSun
              (中略)
        Public Sub RaiseClick ( ByVal Index As Integer )
          cmdWeekBtn( Index ).Item.BackColor = vbRed

 こちらの colWeekBtn は、
      ・ Initialize の中だけでの「使い捨て」の変数
      ・ Initialize 終了後は消えて無くなる
      ・ インスタンス生成時のコーディング手間を省く事だけが目的
 という違いがあります。オブジェクト名を規則的なものにしてあれば、更に、これすらも必要では
 なくなるということです。

 『 (UserForm モジュール全体に渡って必要とされる) コレクションを使わなくても済む』という意味
 と理解してください。




ところで、Excel2000から「RaiseEvent 」という命令がサポートされているのを知っていますか?

  ====================================================================
  次節 : §9  擬似の足枷
  ====================================================================

[ 前頁 , 次頁 , §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.