コレクション内のクラスからイベントを簡単に発生させることができます。問題は、別のクラスが同じクラスの複数からイベントを受け取る直接的な方法がないことです。
clsPeople
通常、イベントを受け取る方法は次のようになります。
Dim WithEvents aPerson As clsPerson
Public Sub AddPerson(p As clsPerson)
Set aPerson = p ' this automagically registers p to the aPerson event-handler `
End Sub
Public Sub aPerson_SelectedChange
...
End Sub
そのため、宣言された変数にオブジェクトを設定すると、オブジェクトがWithEvents
自動的に登録され、その変数のイベント ハンドラーによってイベントが受信されます。 残念ながら、変数は一度に 1 つのオブジェクトしか保持できないため、その変数内の以前のオブジェクトも自動的に登録解除されます。
これに対する解決策 (COM での参照サイクルの問題を回避しながら) は、これに共有デリゲートを使用することです。
したがって、次のようなクラスを作成します。
<<Class clsPersonsDelegate>>
Public Event SelectedChange
Public Sub Raise_SelectedChange
RaiseEvent SelectedChange
End Sub
独自のイベントを発生させたり、すべての親を呼び出す (参照サイクルを作成する) 代わりにSelectedChange
、デリゲート クラスの 1 つのインスタンスですべてのサブを呼び出すようにします。そして、親/コレクション クラスがこの単一のデリゲート オブジェクトからイベントを受け取るようにします。
詳細
このアプローチをどのように使用するかに応じて、さまざまなケースで解決すべき技術的な詳細がたくさんありますが、主なものは次のとおりです。
子オブジェクト (Person) でデリゲートを作成しないでください。親/コンテナー オブジェクト (People) で単一のデリゲートを作成し、コレクションに追加されるときにそれを各子に渡します。子はそれをローカル オブジェクト変数に割り当て、後でそのメソッドを呼び出すことができます。
通常、コレクションのどのメンバーがイベントを発生させたかを知りたいので、型のパラメーターをclsPerson
デリゲート Sub と Event に追加します。次に、デリゲート Sub が呼び出されると、Person オブジェクトはこのパラメーターを介してそれ自体への参照を渡す必要があり、デリゲートはそれを Event を介して親にも渡す必要があります。これは、デリゲートがそのローカル コピーを保存しない限り、参照サイクルの問題を引き起こしません。
親に受信させたいイベントが他にもある場合は、Subs と一致するイベントを同じデリゲート クラスに追加するだけです。
実装例
「親/コンテナー オブジェクト (People) に単一のデリゲートを作成させ、コレクションに追加されるたびにそれを各子に渡す」というより具体的な例の要求に応えます。
これがデリゲート クラスです。呼び出し元の子オブジェクトのパラメーターをメソッドとイベントに追加したことに注意してください。
<<Class clsPersonsDelegate>>
Public Event SelectedChange(obj As clsPerson)
Public Sub RaiseSelectedChange(obj As clsPerson)
RaiseEvent SelectedChange(obj)
End Sub
これが子クラス (Person) です。元のイベントをデリゲートを保持するパブリック変数に置き換えました。また、RaiseEvent をそのイベントのデリゲートのメソッドへの呼び出しに置き換え、それ自体へのオブジェクト ポインターを渡します。
<<Class clsPerson>>
Private pSelected as boolean
'Public Event SelectedChange()'
' Instead of Raising an Event, we will use a delegate'
Public colDelegate As clsPersonsDelegate
Public Property Let Selected (newVal as boolean)
pSelected = newVal
'RaiseEvent SelectedChange'
colDelegate.RaiseSelectedChange(Me)
End Property
Public Property Get Selected as boolean
Selected = pSelected
End Property
そして、これが親/カスタム コレクション クラス (People) です。デリゲートをオブジェクト vairable WithEvents として追加しました (コレクションと同時に作成する必要があります)。また、子オブジェクトをコレクションに追加 (または作成) するときに子オブジェクトのデリゲート プロパティを設定する例の Add メソッドも追加しました。Set item.colDelegate = Nothing
また、コレクションから削除されたときにも対応する必要があります。
<<Class clsPeople>>
Private colPeople as Collection
Private WithEvents colDelegate as clsPersonsDelegate
Private Sub Class_Initialize()
Set colPeople = New Collection
Set colDelegate = New clsPersonsDelegate
End Sub
' Item set as default interface by editing vba source code files'
Public Property Get Item(Index As Variant) As clsPerson
Set Item = colPeople.Item(Index)
End Property
' New Enum set to -4 to enable for ... each to work'
Public Property Get NewEnum() As IUnknown
Set NewEnum = colPeople.[_NewEnum]
End Property
' If selected changes on any person in our collection, do something'
Public Sub colDelegate_SelectedChange(objPerson as clsPerson)
' Do Stuff with objPerson, (just don't make a permanent local copy)'
End Sub
' Add an item to our collection '
Public Sub Add(ExistingItem As clsPerson)
Set ExistingItem.colDelegate = colDelegate
colPeople.Add ExistingItem
' ... '
End Sub