Question Controlling Standard Parent / Children Object Relationships

hallipr

New member
Joined
Nov 10, 2008
Messages
1
Programming Experience
10+
In the past, I have relied on Datasets, LINQ, or other various designers to define my classes and haven't ventured too far into custom classes and collections from scratch, other than simple classes. I would like to at least understand the basics of the standard Parent.Children collection and managing the Child.Parent references associated with the relationship.

Lets start with a basic Author->Books example:

VB.NET:
Public Class BookInfo      
    Public Title As String     
    Public Author As AuthorInfo      
End Class     
     
Public Class AuthorInfo      
    Public Name As String     
    Public Books As New List(of BookInfo)      
End Class


We can create some books and authors, and cross reference them manually:

VB.NET:
'Create Authors   
Dim Author1 As New AuthorInfo   
Author1.Name = "J. Frost"  
  
Dim Author2 As New AuthorInfo   
Author2.Name = "S. Sunny"  
  
'Create Books   
Dim Book1 As New BookInfo   
Book1.Title = "Winter Story"  
  
Dim Book2 As New BookInfo   
Book2.Title = "Summer Things"  
  
Dim Book3 As New BookInfo   
Book3.Title = "Beach Days"  
  
'Cross Reference Authors -> Books   
Book1.Author = Author1   
Book2.Author = Author2   
Book3.Author = Author2   
  
Author1.Books.Add(Book1)   
Author2.Books.Add(Book2)   
Author2.Books.Add(Book3)


This is about as far as I have needed to go up until now. I pulled a bit of code out of a LINQtoSQL class and added it to the BookInfo class:

VB.NET:
Public Class BookInfo
    Public Title As String
    Private _Author As AuthorInfo

    Public Property Author() As AuthorInfo
        Get
            Return _Author
        End Get
        Set(ByVal value As AuthorInfo)
            Dim previousValue As AuthorInfo = Me._Author
            If previousValue IsNot value Then
                'Only proceed if we are indeed changing the books author 
                If previousValue IsNot Nothing Then
                    'If _author was already set, set _author to nothing and remove this book from the author's Books List 
                    Me._Author = Nothing
                    previousValue.Books.Remove(Me)
                End If
                Me._Author = value
                If value IsNot Nothing Then
                    'If we're not setting the author to nothing, add the current book to the authors Books list. 
                    value.Books.Add(Me)
                End If
            End If
        End Set
    End Property
End Class

This allows the book to reference it's author control the authors reference to the book by adding and removing itself from the authors Books collection.

What happens if a book is removed from an AuthorInfo.Books list directly?
We would end up with a Book that still has an AuthorInfo in the _Author field, but the book no longer belongs to the Author's Books collection.

Is it standard to write code on both sides of the relationship to control the references, or should this all be done as code on the parent side responding to events from either the Children collection (AuthorInfo.Books), or the Child (BookInfo) passed up through the collection?

Should a custom BooksList collection be written that passes events like BookRemoved(BookInfo) and AuthorChanged(BookInfo, AuthorInfo)?

It seems like a generic collection should work while still maintaining tight control over the refrences.
 

shawnplr

Active member
Joined
May 17, 2007
Messages
44
Programming Experience
Beginner
I tend to favor generic collections for example I populate a combobox with
VB.NET:
Dim cntrl As Control

    Private Function getControls()
        frmProject.ComboBox1.SelectedItem = Nothing
        frmProject.ComboBox1.Items.Clear()
        For Each ctl As Control In Panel1.Controls
            If TypeOf ctl Is Control Then
                frmProject.ComboBox1.Items.Add(ctl.Name)
                frmProject.ComboBox1.SelectedItem = ctl.Name
            ElseIf ctl.Controls.Count() > 0 Then
                cntrl = getControls(ctl.Name)
            End If
            If Not cntrl Is Nothing Then Exit For
        Next
        Return cntrl
    End Function
This seems to work well in my current project since when a tab is selected the combobox needs to be repopulated with the controls on that tabs form which are inside a control "Panel1".
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,368
Location
Sydney, Australia
Programming Experience
10+
The Collection(Of T) class should be the base for all your custom collections and it can handle this situation for you:
VB.NET:
Public Class Parent

    Private _children As ChildCollection

    Public ReadOnly Property Children() As ChildCollection
        Get
            If Me._children Is Nothing Then
                Me._children = New ChildCollection(Me)
            End If

            Return Me._children
        End Get
    End Property

End Class


Public Class Child

    Private _parent As Parent

    Public Property Parent() As Parent
        Get
            Return Me._parent
        End Get
        Set(ByVal value As Parent)
            If Me._parent IsNot value Then
                If Me._parent IsNot Nothing Then
                    Me._parent.Children.Remove(Me)
                End If

                Me._parent = value

                If value IsNot Nothing AndAlso _
                   Not value.Children.Contains(Me) Then
                    Me._parent.Children.Add(Me)
                End If
            End If
        End Set
    End Property

End Class


Public Class ChildCollection
    Inherits System.Collections.ObjectModel.Collection(Of Child)

    Private parent As Parent

    Public Sub New()
    End Sub

    Public Sub New(ByVal parent As Parent)
        Me.parent = parent
    End Sub

    Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As Child)
        MyBase.InsertItem(index, item)

        item.Parent = Me.parent
    End Sub

    Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As Child)
        Me.Items(index).Parent = Nothing

        MyBase.SetItem(index, item)

        item.Parent = Me.parent
    End Sub

    Protected Overrides Sub RemoveItem(ByVal index As Integer)
        Me.Items(index).Parent = Nothing

        MyBase.RemoveItem(index)
    End Sub

    Protected Overrides Sub ClearItems()
        For Each item As Child In Me.Items
            item.Parent = Nothing
        Next

        MyBase.ClearItems()
    End Sub

End Class
 
Top Bottom