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

  【 擬似からの脱却 】  [ 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)
尚、併せて、コントロール名を「cmdWeek1, cmdWeek2, … ,cmdWeek7」に変更します。
 
 ====== 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
            (中略)
    End Sub

    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
            (中略)
    End Sub

    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.