2013-10-09 54 views
7

如果對象包含在集合中,那麼該對象是否仍然會將事件引發到父類?從自定義集合類中的對象提起事件

很明顯,您可以告訴子類對父類的引用,然後在子類內的父類內調用公共方法,但這會導致循環引用,據我瞭解這會導致它所以垃圾收集器不會擺脫任何一個對象。

詳細信息: 我有兩個類,一個人名爲clsPerson,第二個名爲clsPeople的自定義集合類。 clsPerson有一個名爲Selected的公共布爾屬性。如果選擇更改,我將調用事件SelectedChange。那時候,我需要在clsPeople中做點什麼。我如何在自定義集合類clsPeople中捕獲事件?人類可以從人的範圍之外改變,否則我會看另一個解決方案。

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

回答

8

您可以輕鬆地提高從集合類中的一個事件,問題是,有另一個類沒有直接的方法收到大量來自同一類的倍數事件。

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子的單個實例的代表班。並且您有父/集合類從這個單一委託對象接收事件。

的詳細

有很多的技術細節,制定出針對各種情況,這取決於如何你可以使用這種方法,但這裏是主要的:

  1. 唐沒有子對象(Person)創建委託。讓父/容器對象(人員)創建單個委託,然後將其傳遞給每個孩子,因爲他們被添加到集合中。然後,孩子將它分配給一個本地對象變量,然後它可以稍後調用它的方法。

  2. 通常情況下,你會想知道其中您的收藏成員提出的事件,所以clsPerson類型的參數添加到委託Sub和事件。然後,當委託Sub被調用時,Person對象應該通過這個參數傳遞一個對自身的引用,並且委託也應該通過Event將它傳遞給父對象。只要代理不保存本地副本,這不會導致引用循環問題。

  3. 如果您有更多需要父級接收的事件,只需將更多的Subs和更多匹配的事件添加到同一個委託類。


響應請求的「具有父/容器對象(人)創建單代表,然後作爲它們被添加到集合它傳遞給每個小孩。」更具體的例子

這是我們的委託類。請注意,我已將調用子對象的參數添加到方法和事件中。

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

此代碼對我非常有幫助。它幫助我在VBA中一起使用接口和事件。謝謝您的發佈!在將其付諸實踐後,只需對它進行一些小修改:1)可以從'clsPersonsDelegate'類的方法名稱中刪除文本'Raise_'。 2)'clsPeople'上的'colDelegate_SelectedChange'方法將需要改變以接受類型爲'clsPerson'的參數。此外,對於嘗試此代碼的其他人,'clsPeople'需要一個初始化方法來設置colPeople = New Collection和Set colDelegate = New clsPersonsDelegate。 – BarrettNashville

+1

@BarrettNashville Thx!是的,'Raise_'前綴不是必需的,我只是將它用作風格問題:我喜歡將名稱保持不同,以便我始終知道所引用的內容。 – RBarryYoung