从自定义集合类中的对象引发事件
Posted
技术标签:
【中文标题】从自定义集合类中的对象引发事件【英文标题】:Raising event from object in custom collection class 【发布时间】:2013-10-09 19:08:09 【问题描述】:如果一个对象包含在一个集合中,该对象是否仍然可以向父类引发事件?
显然,您可以告诉子类对父类的引用,然后在子类中调用父类中的公共方法,但这会导致循环引用,据我所知,它会这样垃圾收集器永远不会摆脱任何一个对象。
详情: 我有两个类,一个是名为 clsPerson 的人,第二个是一个名为 clsPeople 的自定义集合类。 clsPerson 有一个名为 Selected 的公共布尔属性。如果 selected 被更改,我将调用一个事件 SelectedChange。那时,我需要在 clsPeople 中做一些事情。如何在自定义集合类 clsPeople 中捕获事件?可以从 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
【问题讨论】:
【参考方案1】:您可以轻松地从集合中的类中引发事件,问题是另一个类无法直接接收来自同一类的多个类的事件。
您的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
子。你让父/集合类从这个单一的委托对象接收事件。
详情
根据您如何使用这种方法,有很多技术细节需要针对各种情况制定,但以下是主要的:
不要让子对象 (Person) 创建委托。让父/容器对象 (People) 创建单个委托,然后在将其添加到集合时将其传递给每个子。然后,孩子会将其分配给本地对象变量,然后可以稍后调用其方法。
通常,您会想知道您的集合中的哪个成员引发了事件,因此将clsPerson
类型的参数添加到委托Sub 和事件中。那么当调用委托Sub时,Person对象应该通过这个参数传递一个对自身的引用,委托也应该通过Event传递给父对象。只要委托不保存它的本地副本,这不会导致引用周期问题。
如果您希望父级接收更多事件,只需将更多 Subs 和更多匹配事件添加到同一个委托类。
示例实现
响应对“让父/容器对象(人员)创建单个委托,然后将其传递给每个孩子,因为它们被添加到集合中。”的更具体示例的请求。 /p>
这是我们的委托类。请注意,我已将调用子对象的参数添加到方法和事件中。
<<Class clsPersonsDelegate>>
Public Event SelectedChange(obj As clsPerson)
Public Sub RaiseSelectedChange(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.RaiseSelectedChange(Me)
End Property
Public Property Get Selected as boolean
Selected = pSelected
End Property
这是我们的父/自定义集合类(人物)。我已将委托添加为对象 vairable WithEvents(它应该与集合同时创建)。我还添加了一个示例 Add 方法,该方法显示在将子对象委托属性添加(或创建)到集合时设置它。从集合中删除时,您还应该有一个对应的Set item.colDelegate = Nothing
。
<<Class clsPeople>>
Private colPeople as Collection
Private WithEvents colDelegate as clsPersonsDelegate
Private Sub Class_Initialize()
Set colPeople = New Collection
Set colDelegate = New clsPersonsDelegate
End Sub
' 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 our 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
【讨论】:
这段代码对我非常有帮助。它帮助我在 VBA 中一起使用接口和事件。谢谢您的发布!付诸实践后,对它进行了一些小的更正: 1) 文本Raise_
可以从clsPersonsDelegate
类中的方法名称中删除。 2) clsPeople
上的 colDelegate_SelectedChange
方法需要更改为接受 clsPerson
类型的参数。此外,对于尝试此代码的其他人,clsPeople
将需要 Set colPeople = New Collection
和 Set colDelegate = New clsPersonsDelegate
的初始化方法。
@BarrettNashville 谢谢!是的,Raise_
前缀不是必需的,我只是将其用作风格问题:我喜欢保持名称不同,以便我始终知道所指的是哪个。
同上 BarrettNashville 的 cmets,对我也非常有帮助。谢谢。
非常感谢!以上是关于从自定义集合类中的对象引发事件的主要内容,如果未能解决你的问题,请参考以下文章
Vue:从自定义组件派生的自定义组件中的 v-model 和输入事件