如果对象包含在集合中,那么该对象是否仍然可以将事件引发到父类?
显然,您可以告诉子类对父类的引用,然后在子类中的父类中调用公共方法,但这会导致循环引用,据我所知它会使它成为垃圾收集器永远不会摆脱任何一个对象.
细节:我有两个类,一个是名为clsPerson的人,另一个是名为clsPeople的自定义集合类.clsPerson有一个名为Selected的公共布尔属性.如果选择了更改,我会调用一个事件SelectedChange.那时,我需要在clsPeople做点什么.如何在自定义集合类clsPeople中捕获事件?person类可以从People的范围之外更改,否则我会查看另一个解决方案.
<<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
Run Code Online (Sandbox Code Playgroud)
RBa*_*ung 12
您可以轻松地从集合中的类中引发事件,问题在于,没有直接的方法让另一个类从同一个类的多个事件中接收事件.
您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
Run Code Online (Sandbox Code Playgroud)
因此,将对象设置为声明的任何变量会WithEvents自动注册它,以便该变量的事件处理程序接收它的事件. 不幸的是,变量一次只能容纳一个对象,因此该变量中的任何先前对象也会自动取消注册.
对此的解决方案(同时仍然避免COM中的引用循环的问题)是为此使用共享委托.
所以你创建一个这样的类:
<<Class clsPersonsDelegate>>
Public Event SelectedChange
Public Sub Raise_SelectedChange
RaiseEvent SelectedChange
End Sub
Run Code Online (Sandbox Code Playgroud)
现在,不是提升自己的事件或者都调用它们的父项(进行引用循环),而是让它们SelectedChange在委托类的单个实例中调用sub.并且您有父/集合类从此单个委托对象接收事件.
细节
根据您使用这种方法的方式,有很多技术细节可以解决各种情况,但主要有以下几种:
不要让子对象(Person)创建委托.让父/容器对象(People)创建单个委托,然后在将它们添加到集合时将其传递给每个子节点.然后,子进程将其分配给本地对象变量,该变量的方法随后可以调用.
通常,您需要知道集合的哪个成员引发了事件,因此clsPerson向委托Sub和Event 添加类型参数.然后,当调用委托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
Run Code Online (Sandbox Code Playgroud)
这是我们的子类(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
Run Code Online (Sandbox Code Playgroud)
这是我们的父/自定义集合类(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(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
Run Code Online (Sandbox Code Playgroud)