在自定义集合类中从对象提升事件 [英] Raising event from object in custom collection class

查看:85
本文介绍了在自定义集合类中从对象提升事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果某个对象包含在集合中,该对象是否仍会将事件引发到父类?

If an object is contained within a collection, can that object still raise events to a parent class?

显然,您可以告诉孩子类对父类的引用,然后在子类中的父类中调用一个公共方法,但这会导致循环引用,据了解,这将使垃圾收集器无法摆脱任何一个对象。

Clearly you could tell the child class a reference to the parent class, and then call a public method within the parent class within the child class, however that would result in a circular reference, which as I understand it would make it so the garbage collector would not ever get rid of either object.

详细信息:
我有两个类,一个名叫clsPerson的人,第二个是名为clsPeople的自定义集合类。 clsPerson具有名为Selected的公共布尔属性。如果选择被更改,我调用一个事件SelectedChange。在这一点上,我需要在clsPeople中做一些事情。如何在自定义集合类clsPeople中捕获事件?人类可以从人的范围之外改变,否则我会看另一个解决方案。

Details: I have two classes, one a person named clsPerson, and the second a custom collection class named clsPeople. clsPerson has a public boolean property named Selected. If selected is changed, I call an event SelectedChange. At that point, I need to do something in clsPeople. How can I trap the event in the custom collection class clsPeople? The person class can be changed from outside of the scope of People, otherwise I would look at another solution.

<<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


推荐答案

一个集合中的类的事件,问题是没有直接的方式可以让另一个类从同一个类的多个字节接收事件。

You can easily raise an event from a class in a collection, the problem is that there's no direct way for another class to receive events from multiples of the same class.

您的 clsPeople 通常会收到该事件的方式如下:

The way that your clsPeople would normally receive the event would be like this:

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

所以将对象设置为声明为$ code的任何变量WithEvents 自动注册它,以便事件将被该变量的事件处理程序接收。 ,一个变量一次只能保存一个对象,所以该变量中的任何先前的对象也会被自动取消注册。

So setting an object into any variable declared WithEvents automatically registers it so that it's events will be received by that variable's event handlers. Unfortunately, a variable can only hold one object at a time, so any previous object in that variable also gets automatically de-registered.

解决这个问题(同时仍然避免COM中引用循环的问题)是为此使用一个共享委托。

The solution to this (while still avoiding the problems of reference cycles in COM) is to use a shared delegate for this.

所以你这样做一个类:

<<Class clsPersonsDelegate>>

Public Event SelectedChange

Public Sub Raise_SelectedChange
    RaiseEvent SelectedChange
End Sub

现在,您可以将它们全部调用为 SelectedChange sub在委托类的单个实例中。而且您有父/收集类接收来自此单一代理对象的事件。

Now instead of raising their own event or all calling their parent (making a reference cycle), you have them all call the SelectedChange sub in a single instance of the delegate class. And you have the parent/collection class receive events from this single delegate object.

详细信息

有很多技术细节可以解决各种情况,具体取决于您可以如何使用这种方法,但主要是:

There are a lot of technical details to work out for various cases, depending on how you may use this approach, but here are the main ones:


  1. 没有子对象(Person)创建委托。让父/容器对象(People)创建单个委托,然后将它们传递给每个孩子,因为它们被添加到集合中。然后,孩子会将其分配给一个本地对象变量,然后可以稍后调用其方法。

  1. Don't have the child objects (Person) create the delegate. Have the parent/container object (People) create the single delegate and then pass it to each child as they are added to the collection. The child would then assign it to a local object variable, whose methods it can then call later.

通常,您将要知道您的集合的成员引发了事件,所以添加一个类型为 clsPerson 的参数到委托Sub和事件。然后当委托Sub被调用时,Person对象应通过该参数传递对自身的引用,代理也应该通过事件将其传递给父进程。这不会引起参考周期问题,只要委托人不保存本地副本。

Typically, you will want to know which member of your collection raised the event, so add a parameter of type clsPerson to the delegate Sub and the Event. Then when the delegate Sub is called, the Person object should pass a reference to itself through this parameter, and the delegate should also pass it along to the parent through the Event. This does not cause reference-cycle problems so long as the delegate does not save a local copy of it.

如果您有更多的事件,您想要父要接收,只需将更多的辅助和更多匹配的事件添加到同一代表类。

If you have more events that you want the parent to receive, just add more Subs and more matching Events to the same delegate class.






响应请求更具体的例子让父/容器对象(People)创建单个委托,然后将它们传递给每个孩子,因为它们被添加到集合中。

这是我们的委托类。请注意,我已将调用子对象的参数添加到方法和事件。

Here's our delegate class. Notice that I've added the parameter for the calling child object to the method and the event.

<<Class clsPersonsDelegate>>

Public Event SelectedChange(obj As clsPerson)

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

这是我们的小孩类(Person)。我已经用原来的事件替换了一个公共变量来保存代理。我也替换了RaiseEvent,通过调用该事件的代理方法,将一个对象指针传递给自己。

Here's our child class (Person). I have replaced the original event, with a public variable to hold the delegate. I have also replaced the RaiseEvent with a call to the delegate's method for that event, passing along an object pointer to itself.

<<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

这是我们的父/个人收藏类(人)。我已经将代理添加为可执行的WithEvents对象(它应该与集合同时创建)。我还添加了一个示例Add方法,显示在向集合添加(或创建)时设置子对象委托属性。您还应该有一个相应的将item.colDelegate = Nothing 从集合中删除。

And here's our parent/custom-collection class (People). I have added the delegate as an object vairable WithEvents (it should be created at the same time as the collection). I have also added an example Add method that shows setting the child objects delegate property when you add (or create) it to the collection. You should also have a corresponding Set item.colDelegate = Nothing when it is removed from the collection.

<<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

这篇关于在自定义集合类中从对象提升事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆