2013-10-09 9 views
7

オブジェクトがコレクション内に含まれている場合、そのオブジェクトは引き続きイベントを親クラスに発行できますか?カスタムコレクションクラスのオブジェクトからイベントを呼び出す

明らかに、子クラスに親クラスへの参照を伝えてから、子クラス内の親クラス内でパブリックメソッドを呼び出すことはできますが、循環参照になります。ガベージコレクタはどちらのオブジェクトも削除しませんでした。

詳細: 私は2つのクラス、1つはclsPersonという名前、もう1つはclsPeopleという名前のカスタムコレクションクラスを持っています。 clsPersonには、Selectedという名前のpublic booleanプロパティがあります。選択されている場合は、SelectedChangeイベントを呼び出します。その時点で、私はclsPeopleで何かする必要があります。カスタムコレクションクラスclsPeopleでイベントをトラップするにはどうすればいいですか?人のクラスは人の範囲外から変更することができます。そうでなければ、私は別の解決策を探します。

<<Class clsPerson>> 
Private pSelected as boolean 

Public Event SelectedChange() 

Public Property Let Selected (newVal as boolean) 
    pSelected = newVal 
    RaiseEvent SelectedChange 
End Property 

Public Property Get Selected as boolean 
    Selected = pSelected 
End Property 

<<Class clsPeople>> 
Private colPeople as Collection 

' 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 a person, do something 
Public Sub ???_SelectedChange 
    ' Do Stuff 
End Sub 

答えて

8

あなたは簡単にコレクション内のクラスからイベントを発生させることができますが、問題があっに、別のクラスのための直接的な方法ません、同じクラスの倍数からイベントを受け取るということです。

あなた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)に1つのデリゲートを作成させ、それをコレクションに追加するときに各子に渡します。子はそれをローカルオブジェクト変数に代入し、そのメソッドは後で呼び出すことができます。

  2. 通常、あなたはあなたのコレクションのメンバーがイベントを発生させたを知りたいので、デリゲートサブやイベントにタイプclsPersonのパラメータを追加します。次に、委譲Subが呼び出されると、Personオブジェクトはこのパラメータを介して自身への参照を渡す必要があり、代理人はEventを介してそれを親に渡す必要があります。デリゲートがローカルコピーを保存しない限り、参照サイクルの問題は発生しません。

  3. 親が受け取る予定のイベントがさらにある場合は、さらに多くのサブイベントと一致するイベントを同じデリゲートクラスに追加してください。 「親/コンテナオブジェクト(ピープル)は、単一のデリゲートを作成し、その後、彼らはコレクションに追加されているとして、それぞれの子に渡してもらいます。」のより具体的な例のための要求に応答


ここに私たちの代表クラスがあります。呼び出し側の子オブジェクトのパラメータをメソッドとイベントに追加したことに注目してください。

<<Class clsPersonsDelegate>> 

Public Event SelectedChange(obj As clsPerson) 

Public Sub Raise_SelectedChange(obj As clsPerson) 
    RaiseEvent SelectedChange(obj) 
End Sub 

ここに私たちの子クラス(Person)があります。私は元のイベントを代理人を保持するためのパブリック変数に置き換えました。私はRaiseEventをそのイベントのデリゲートのメソッドへの呼び出しで置き換え、オブジェクトポインタを渡します。

ここでは、親/カスタムコレクションクラス(人物)を示します。デリゲートをオブジェクトの可搬WithEvents(コレクションと同時に作成する必要があります)として追加しました。また、コレクションに追加(または作成)するときに子オブジェクトデリゲートプロパティを設定する方法を示すAddメソッドも追加しました。コレクションから削除すると対応するSet item.colDelegate = Nothingも必要です。

<<Class clsPeople>> 
Private colPeople as Collection 
Private WithEvents colDelegate as clsPersonsDelegate 

' 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 out collection, do something' 
Public Sub colDelegate_SelectedChange 
    ' Do Stuff' 
End Sub 

' Add an item to our collection ' 
Public Sub Add(ExistingItem As clsPerson) 
    Set ExistingItem.colDelegate = colDelegate 
    colPeople.Add ExistingItem 

    ' ... ' 
End Sub 
+2

このコードは非常に役に立ちました。 VBAでインターフェイスとイベントを一緒に使用するのに役立ちました。投稿していただきありがとうございます! 1)テキスト 'Raise_'は' clsPersonsDelegate'クラスのメソッド名から削除することができます。 2) 'clsPeople'の' colDelegate_SelectedChange'メソッドは、 'clsPerson'型のパラメータを受け入れるように変更する必要があります。また、このコードを試してみる人のために、 'clsPeople'は' Set colPeople = New Collection'と 'Set colDelegate = New clsPersonsDelegate'にinitializeメソッドが必要です。 – BarrettNashville

+1

@BarrettNashville Thx!はい、 'Raise_'接頭辞は必要ではありません、私はちょうどスタイルの問題としてそれを使用します:私はいつも私が参照されていることを知るように別名を保つのが好きです。 – RBarryYoung

関連する問題