Custom Object Display & Modification

corteplaneta

Member
Joined
Nov 14, 2008
Messages
10
Programming Experience
3-5
Hello,

I'm new here, but being this is a VB.NET specific forum, I guess it's the right place :).

I'm running VS2008 & attempting to solve a problem I've encountered while developing some software for our business.

Here's the basic idea...I've created a class that represents a file (with members such as file name, file location, and a string containing the files data). Each file object also contains an ArrayList of another custom object I've created--a field object (which is basically two fields--field name & field data). These fields are extracted from the file (as the file is a HTML document), and are simply text within bracket [ExampleField].

What I'm attempting to do is, every time a file is loaded, create a file object & the array of fields extracted contained within this file object, and display this to the user.

I've attempted using a ListView to accomplish this, to no avail, as I don't believe I can correctly & dynamically link an ArrayList of custom objects to a ListView control.

I want the user to be able to select an object in the list which corelates to a certain instance of the file class. Once the user selects this object, I want to be able to pass a reference to this instance of the file object as an optional parameter to another form which, in turn, extracts the ArrayList of field objects & allows the user to edit these fields.

I re-wrote all of my code due to the lack of OO, and now I've hit the same brick wall as I did in the beginning.

Does anyone know of an efficient way of doing what I'm attempting to accomplish? Any help, thoughts, etc, would be greatly appreciated!!

Thanks!

-corteplaneta
 
You should not be using the ArrayList class at all in VB 2008. If you're only using the collection within the object it's created in then you should use a generic List:
VB.NET:
Public Class FileObject

    Private fieldObjects As New List(Of FieldObject)

    '...

End Class
If you're exposing it outside the object it's created in then you should define your own strongly-typed collection class:
VB.NET:
Public Class FieldObjectCollection
    Inherits System.Collections.ObjectModel.Collection(Of FieldObject)
End Class
and then use that:
VB.NET:
Public Class FieldObject

    Private _fieldObjects As New FieldObjectCollection

    Public ReadOnly Property FieldObjects() As FieldObjectCollection
        Get
            Return Me._fieldObjects
        End Get
    End Property

    '...

End Class
I would also suggest using lazy loading, so the actual collection is not created until, and unless, it's actually needed:
VB.NET:
Public Class FieldObject

    Private _fieldObjects As FieldObjectCollection

    Public ReadOnly Property FieldObjects() As FieldObjectCollection
        Get
            If Me._fieldObjects Is Nothing Then
                Me._fieldObjects = New FieldObjectCollection

                'Populate collection here.
            End If

            Return Me._fieldObjects
        End Get
    End Property

    '...

End Class
Now, when you create your editing form you would declare the constructor with a parameter of that type:
VB.NET:
Public Class Form1

    Public Sub New(ByVal fieldObjects As FieldObjectCollection)
        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

        'Display field objects here.
    End Sub

    '...

End Class
You might display the collection of FieldObjects by binding it to a DataGridView, or some other way if that's not appropriate.

To edit the fields of a FileObject you'd do this:
VB.NET:
Using f1 As New Form1(myFileObject.FieldObjects)
    f1.ShowDialog()
End Using
Because you're accessing the FieldObjects property when you create the editing form, the collection will be created at that time if it doesn't already exist.
 
Alright, I've gotten my strongly typed collection created, and bound this to a DataGridView control at run-time. The problem I'm having now is that I'm unable to update the contents of the control.

If I first add items to my collection, then set the DataGridView's datasource as the collection, I am able to view these items in the DataGridView.

The problem is that when I add an item to my collection & attempt to call the DataGridView1.update() method, nothing happens, though when I iterate through the collection the object is definitely being added.

How can I ensure that the collection will be updated when I add new items to it?

Thanks in advance.

-corteplaneta
 
As it is your collection provides no notification that an item has been added so the grid has no reason to go looking for more data. Calling Update on the grid simply redraws it and nothing more. It will simply redraw itself with the old data because it has no idea there is new data to get.

If you want to add an item to your collection directly and expect a bound control to update automatically then your collection must implement the IBindingList interface. It can then raise its ListChanged event when data is added, edited or removed and any bound controls will update automatically.

You should be binding your data via a BindingSource. If you do that, you can add an item to the collection by calling the AddNew method of the BindingSource. That way the bound control will update automatically whether your collection implements IBindingList or not, because the BindingSource does and it will raise its ListChanged event.

If you bind your collection to a BindingSource then you can still add items to the collection directly. If your collection implements IBindingList then it will raise its ListChanged event, the BindingSource will handle that and raise its own ListChanged event, which the bound control will handle and then update automatically. If your collection doesn't implement IBindingList then you can explicitly call ResetBindings on the BindingSource and force a refresh. There's also a ResetItem method if you edit a single item.
 
Wow, once again exactly what I was looking for.

I've got a bit of an amateur question for you now...I attempted to implement the IBindingList interface for my class and I was simply unable to figure this out. I'm rather a newbie at VB.NET, and have never implemented an interface before, so please bear with me.

What exactly would the ListChanged event need to notify the BindingSource of, and how would this be implemented?
 
The ListChanged event should be raised whenever the list changes, i.e. an item is added, edited or removed. Your collection class should look something like this:
VB.NET:
Public Class ThingCollection
    Inherits BindingList(Of Thing)

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

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    End Sub

    Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As Thing)
        MyBase.SetItem(index, item)

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    End Sub

    Protected Overrides Sub RemoveItem(ByVal index As Integer)
        MyBase.RemoveItem(index)

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    End Sub

    Protected Overrides Sub ClearItems()
        MyBase.ClearItems()

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, 0))
    End Sub

End Class
In order to notify of changes to the items themselves the collection must handle the appropriate events of those items, then raise the ListChanged event with a ListChangedEventArgs object with its ListChangeType property set to ItemChanged. In order to do that you have to use the AddHandler statement in the InsertItem and SetItem methods and the RemoveHandlerstatement in the SetItem, RemoveItem and Clear methods. Here's and extended example that shows that:
VB.NET:
Public Class ThingCollection
    Inherits BindingList(Of Thing)

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

        AddHandler item.SomePropertyChanged, AddressOf HandleSomePropertyChanged

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    End Sub

    Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As Thing)
        RemoveHandler Me.Items(index).SomePropertyChanged, AddressOf HandleSomePropertyChanged

        MyBase.SetItem(index, item)

        AddHandler item.SomePropertyChanged, AddressOf HandleSomePropertyChanged

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    End Sub

    Protected Overrides Sub RemoveItem(ByVal index As Integer)
        RemoveHandler Me.Items(index).SomePropertyChanged, AddressOf HandleSomePropertyChanged

        MyBase.RemoveItem(index)

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    End Sub

    Protected Overrides Sub ClearItems()
        For Each Item As Thing In Me.Items
            RemoveHandler Item.SomePropertyChanged, AddressOf HandleSomePropertyChanged
        Next

        MyBase.ClearItems()

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, 0))
    End Sub

    Private Sub HandleSomePropertyChanged(ByVal sender As Object, ByVal e As EventArgs)
        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemChanged, _
                                                  Me.Items.IndexOf(DirectCast(sender,  _
                                                                              Thing))))
    End Sub

End Class
If the items have more events that indicate property changes then you'd need to handle them too.
 
Back
Top