如果对象包含在集合中,该对象是否仍然可以将事件引发到父类?
显然,你可以告诉子类对父类的引用,然后在子类中的父类中调用一个公共方法,但这会导致循环引用,据我所知它会使它成为垃圾收集器永远不会摆脱任何一个对象。
详细说明: 我有两个类,一个是名为clsPerson的人,另一个是名为clsPeople的自定义集合类。 clsPerson有一个名为Selected的公共布尔属性。如果选择了更改,我会调用一个事件SelectedChange。那时,我需要在clsPeople做点什么。如何在自定义集合类clsPeople中捕获事件?可以从People的范围之外更改person类,否则我会查看另一个解决方案。
<<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
答案 0 :(得分:10)
您可以轻松地从集合中的某个类中引发事件,问题是另一个类没有直接方法从同一个类的多个接收事件。
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
的任何变量会自动注册它,以便该变量的事件处理程序接收它的事件。 不幸的是,变量一次只能容纳一个对象,因此该变量中的任何先前对象也会自动取消注册。
对此的解决方案(虽然仍然避免了COM中的引用循环问题)是为此使用共享委托。
所以你创建了这样一个类:
<<Class clsPersonsDelegate>>
Public Event SelectedChange
Public Sub Raise_SelectedChange
RaiseEvent SelectedChange
End Sub
现在不是提升自己的事件或者都调用它们的父(创建引用循环),而是让它们在委托类的单个实例中调用SelectedChange
sub。并且您有父/集合类从此单个委托对象接收事件。
详情
根据您使用这种方法的方式,有很多技术细节需要解决,但主要有:
不要让子对象(Person)创建委托。让父/容器对象(People)创建单个委托,然后在将它们添加到集合时将其传递给每个子节点。然后,子进程将其分配给本地对象变量,该变量的方法随后可以调用。
通常,您需要知道您的集合的哪个成员引发了该事件,因此请向委托Sub和Event添加类型为clsPerson
的参数。然后,当调用委托Sub时,Person对象应该通过此参数传递对自身的引用,并且委托也应该通过Event将它传递给父对象。只要委托不保存它的本地副本,这不会导致引用循环问题。
如果您希望父级接收更多事件,只需将更多Subs和更多匹配的事件添加到同一个委托类。
响应对“让父/容器对象(People)创建单个委托的更具体示例的请求,然后将它们传递给每个子项,因为它们被添加到集合中。”
这是我们的委托课程。请注意,我已将调用子对象的参数添加到方法和事件中。
<<Class clsPersonsDelegate>>
Public Event SelectedChange(obj As clsPerson)
Public Sub Raise_SelectedChange(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.SelectedChange(Me)
End Property
Public Property Get Selected as boolean
Selected = pSelected
End Property
这是我们的父/自定义集合类(People)。我已将委托添加为可变的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