从自定义集合类中的对象引发事件

lfr*_*dom 9 events vba

如果对象包含在集合中,那么该对象是否仍然可以将事件引发到父类?

显然,您可以告诉子类对父类的引用,然后在子类中的父类中调用公共方法,但这会导致循环引用,据我所知它会使它成为垃圾收集器永远不会摆脱任何一个对象.

细节:我有两个类,一个是名为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.并且您有父/集合类从此单个委托对象接收事件.

细节

根据您使用这种方法的方式,有很多技术细节可以解决各种情况,但主要有以下几种:

  1. 不要让子对象(Person)创建委托.让父/容器对象(People)创建单个委托,然后在将它们添加到集合时将其传递给每个子节点.然后,子进程将其分配给本地对象变量,该变量的方法随后可以调用.

  2. 通常,您需要知道集合的哪个成员引发了事件,因此clsPerson向委托Sub和Event 添加类型参数.然后,当调用委托Sub时,Person对象应该通过此参数传递对自身的引用,并且委托也应该通过Event将它传递给父对象.只要委托不保存它的本地副本,这不会导致引用循环问题.

  3. 如果您希望父级接收更多事件,只需将更多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)

  • 这段代码对我非常有帮助.它帮助我在VBA中一起使用接口和事件.谢谢您的发布!在将其付诸实践后,只需对它进行一些小修正:1)可以从`clsPersonsDelegate`类中的方法名称中删除文本`Raise_`.2)`clsPeople`上的`colDelegate_SelectedChange`方法需要更改为接受`clsPerson`类型的参数.此外,对于其他尝试此代码的人来说,`clsPeople`将需要一个初始化方法来设置`set colPeople = New Collection`和`Set colDelegate = New clsPersonsDelegate`. (2认同)