Windows Develop Bookmark and Share   
 index > Windows Forms Data Controls and Databinding > Binding ArrayList (of Structures) to a Datagrid Question?
 

Binding ArrayList (of Structures) to a Datagrid Question?

OK. I am **hoping** this is something silly and simple that I'm forgetting to do. I've read some examples on binding a datagrid to an array list and that is what I'm now trying to do. I've got an ArrayList (populated from a structure). I've got the MappingName set to the ArrayList name (which I believe is what I'm supposed to do). I've got each of the items in the ArrayList all as a String which I've also read is necessary (all the ArrayList items have to be of the same type). But I'm not getting a datagrid with the information in it.

Here's what I've got so far ... can anyone see where I'm going wrong?

********

'    structure which will be loaded into the array
Public Structure Data

    Public _ID As Integer
    Public _dt As DateTime
    Public _text1 As String
    Public _num As Decimal
    Public _text2 As String
    Public _text3 As String
    Public _state As Boolean

    Public Sub New(ByVal id, ByVal dt, ByVal text1, ByVal num, _
            ByVal text2, ByVal text3, ByVal state)

        _ID = id
        _dt = dt
        _Text1 = text1
        _num = num
        _text2 = text2
        _text3 = text3
        _state = state

    End Sub

End Structure



'    this is where I add the structure to the arraylist and then call
'    the procedure to display the arraylist in the datagrid
Private Sub AddLIArrayItem()

    Dim temp As New Data(intServID.ToString, TextBox1.Text.ToString, _
            TextBox2.Text.ToString, TextBox3.Text.ToString, _
            TextBox4.Text.ToString, TextBox5.Text.ToString, _
            Check1.checked.ToString)

     arrlstFullList.Add(temp)

     BindArrayListToGrid()

End Sub



Private Sub BindArrayListToGrid()

    DataGrid1.DataSource = arrlstLineItems

    Dim ts1 As DataGridTableStyle
    ts1 = New DataGridTableStyle
    ts1.MappingName = "arrlstFullList"

    Dim col0 As DataGridTextBoxColumn
    col0 = New DataGridTextBoxColumn
    With col0
        .MappingName = "_ID"
        .Width = 65
    End With
    ts1.GridColumnStyles.Add(col0)

    Dim col1 As DataGridTextBoxColumn
    col1 = New DataGridTextBoxColumn
    With col1
        .MappingName = "_dt"
        .Width = 65
    End With
    ts1.GridColumnStyles.Add(col1)

'    I have code for the next 5 columns (numbers 2 - 6) listed
'    here but since they are all the same (just diff. widths) other
'    than the mappingname, I figured I would just list the first
'    two.  For each of the columns I do change the .MappingName
'    to match the structure name for that item (_ID, _text1, _num,
'    etc.)

    DataGrid1.TableStyles.Clear()
    DataGrid1.TableStyles.Add(ts1)

End Sub

********

I am lost at this point. I get nothing but a gray box when I run the code. I am REALLY hoping that it is something simple that I'm missing. As always, any and all help is greatly appreciated.

Thanks in advance.
MigrationUser 1  Monday, May 09, 2005 1:06 PM
At a glance, that looks about right... I'm not sure about using an array of structures though...  I'm not sure if databinding can modify values without get/set statements.

This is the kind of stuff that makes binding to an ArrayList more trouble than it is worth (in my opinion).  It is pretty easy to create your own strongly-typed collection for a cusotm object and well worth the effort.

I'm going to post an example that I wrote for some Visual RPG guys to get a handle around custom collections and relations.  This example is going to build three objects (Customer, Order, and Item) and a strongly typed collection for each.  The Customer object will contain an Order collection object and the Order object will contain an Item collection object.  This allows the user to create instances of the Item object, add them to the Item collection on a Order object and then add the Order to the Order collection on the Customer object.  You can then set the DataSource of a DataGrid to the Customer object and voila!  The grid shows the customers and their properties and allows you to drill down into the orders and items.

You can create a new class file in a VB project and paste this code over the code page.  You also probably want to use Find-Replace to remove all occurances of "MultidimensionalCollectionExample.", which was the original assembly name of this project:

'Declare namespaces, or named container, to seperate the 
'Objects from their respective Collections
Namespace ObjectClasses

    'Create a class for each table of related data.
    'This example will use Customers, which contain
    'Orders, which contain Items.
#Region " Customer Object " 'Use regions to organize code

    'Define a CustomerObject to hold the data about each
    'customer as we read it from the database
    Public Class CustomerObject

        'Use private variables declared at the class level
        'to store the data for the public properties we wish
        'to expose. Initialize these variables by assigning
        'values to them at creation.  While not strictly
        'necessary as used in this context, if we later wish
        'to fully support DataBinding these vars are ready.
        Private myCustomerID As Integer = -1
        Private myName As String = ""
        Private myPhone As Integer = -1
        'Notice that this variable is a class, not just a
        'structure, so we have to create an instance of it instead
        'of just assigning it a value.
        Private myOrders As New CollectionClasses.OrderCollection

        'Data validation can be written right into the properties
        'themselves. To take advantage of this, define public
        'events that your class can raise to notify the calling
        'code that something has happend or is wrong. This example
        'is going to require a seven position phone number and
        'raise the following event if the user enters any other
        'number of characters. Notice the event follows the
        'standard event design with a generic sender object an
        'and a single event arguments object that inherits
        'from Sytem.EventArgs... more on this later
        Public Event PhoneError(ByVal sender As Object, ByVal e As MultidimensionalCollectionExample.ObjectClasses.CustomerObject.PhoneErrorEventArgs)

        'It's necessary to include a Public Sub New() in order
        'for a class to be instantiated (create an "Instanced
        'Class"). Since we've given our internal variables
        'values at creation, we don't need to actually do
        'anything in the routine.
        Public Sub New()

        End Sub

        'Providing multiple Sub New() methods allows the 
        'object to be declared in different ways.  Each Sub New()
        'method requires a unique set of arguments.  Sub New() is
        'the only class element that doesn't require the Overloads
        'keyword.
        Public Sub New(ByVal CustomerID As Integer, ByVal Name As String, ByVal Phone As Integer)
            myCustomerID = CustomerID
            myName = Name
            myPhone = Phone
            myOrders = New CollectionClasses.OrderCollection
        End Sub

        'Define a property for each field in your DataSource.
        'Each property requires a Get and Set statement that
        'determine the actions taken when a calling code routine
        'reads or writes to the property value. The exceptions
        'are properties declared ReadOnly or WriteOnly; they
        'only use the Get or Set statement as appropriate.
        Public Property CustomerID() As Integer
            Get
                'This Get statement simply returns the
                'private CustomerID variable
                Return myCustomerID
            End Get
            'The Set statement always contains an argument of
            '"Value" which must be the same data type as the
            'property. Value will contain the data set by
            'the calling class.
            Set(ByVal Value As Integer)
                'Set the private CustomerID to Value
                myCustomerID = Value
            End Set
        End Property

        Public Property Name() As String
            Get
                Return myName
            End Get
            Set(ByVal Value As String)
                myName = Value
            End Set
        End Property

        'This property shows a simple example of providing
        'data validation within a Set statement. This can
        'be an effecient way to validate in that multiple forms
        'can change a phone number and the validation code
        'doesn't need to be written on each form.
        Public Property Phone() As Integer
            Get
                Return myPhone
            End Get
            Set(ByVal Value As Integer)
                'Rather than blindly set the private Phone
                'variable to the value of Value, perform
                'any desired data validation first.

                'This code converts the absoloute value of
                'the Integer to a string and checks it's length
                If System.Math.Abs(Value).ToString.Length = 7 Then
                    'If the length is 7 then set the variable
                    myPhone = Value
                Else
                    'If it's not 7 then raise the PhoneError event
                    'When raising the event, create a new instance
                    'of the custom EventArgs object and provide
                    'any necessary information about what is wrong
                    RaiseEvent PhoneError(Me, New MultidimensionalCollectionExample.ObjectClasses.CustomerObject.PhoneErrorEventArgs(Value, "Number of digits does not equal seven"))
                End If
            End Set
        End Property

        'When collections are exposed as properties they are
        'often declared ReadOnly so that the entire collection
        'cannot be replaced. Items can still be fully 
        'manipulated (add, modify, remove) within the collection.
        'ReadOnly properties do not have a Set statement.
        Public ReadOnly Property Orders() As CollectionClasses.OrderCollection
            Get
                Return myOrders
            End Get
        End Property

        'The value of a property can be calculated on the fly.
        'These properties have no need of a private variable.
        'They also must be declared ReadOnly as it would not
        'be logical to set a dynamically built property.
        Public ReadOnly Property PurchaseTotal() As Double
            'This property will add the total of each order total
            'in the customer object's Order collection.
            Get
                'Declare a variable to hold the return value
                Dim pt As Double = 0
                'Get an enumerator for the Order collection
                Dim enm As IEnumerator = myOrders.GetEnumerator
                'Move through each order object in the collection
                While enm.MoveNext
                    'Cast the current object to an Order object
                    'and add it's OrderTotal property value to
                    'the return value
                    pt += CType(enm.Current, MultidimensionalCollectionExample.ObjectClasses.OrderObject).OrderTotal
                End While
                'Return the calculated value
                Return pt
            End Get
        End Property

        'It is also possible to expose an underlying property
        'value within a class.  This property exposes the Count
        'property of the Order collection. This is an easy way
        'to get extra infomation into your datagrid.  Exposed
        'properties should be declared in the same manner as
        'the exposing class.
        Public ReadOnly Property PurchaseCount() As Integer
            Get
                Return myOrders.Count
            End Get
        End Property

#Region " Custom EventArgs "

        'Within the Order class define any needed EventArg classes
        Public Class PhoneErrorEventArgs
            'Inherit from the base EventArgs class
            Inherits System.EventArgs

            'Provide properties for each data value that needs
            'to be passed when raising the event
            Private myAttemptedNumber As Integer
            Private myProblem As String

            Public Sub New(ByVal AttemptedNumber As Integer, ByVal Problem As String)
                myAttemptedNumber = AttemptedNumber
                myProblem = Problem
            End Sub

            Public ReadOnly Property AttemptedNumber() As Integer
                Get
                    Return myAttemptedNumber
                End Get
            End Property

            Public ReadOnly Property Problem() As String
                Get
                    Return myProblem
                End Get
            End Property

        End Class

#End Region

    End Class

#End Region

#Region " Order Object "

    Public Class OrderObject

        Private myOrderID As Integer = -1
        Private myItems As CollectionClasses.ItemCollection

        Public Sub New(ByVal OrderID As Integer)
            myOrderID = OrderID
            myItems = New CollectionClasses.ItemCollection
        End Sub

        Public Property OrderID() As Integer
            Get
                Return myOrderID
            End Get
            Set(ByVal Value As Integer)
                myOrderID = Value
            End Set
        End Property

        Public ReadOnly Property Items() As CollectionClasses.ItemCollection
            Get
                Return myItems
            End Get
        End Property

        Public ReadOnly Property OrderTotal() As Double
            Get
                Dim FoundTotal As Double = 0
                Dim enm As IEnumerator = myItems.GetEnumerator
                While enm.MoveNext
                    FoundTotal += CType(enm.Current, ObjectClasses.ItemObject).Price
                End While
                Return FoundTotal
            End Get
        End Property

    End Class

#End Region

#Region " Item Object "

    Public Class ItemObject

        Private myItemID As Integer = -1
        Private myName As String = ""
        Private myPrice As Double = 0

        Public Sub New(ByVal ItemID As Integer, ByVal Name As String, ByVal Price As Double)
            myItemID = ItemID
            myName = Name
            myPrice = Price
        End Sub

        Public Property ItemID() As Integer
            Get
                Return myItemID
            End Get
            Set(ByVal Value As Integer)
                myItemID = Value
            End Set
        End Property

        Public Property Name() As String
            Get
                Return myName
            End Get
            Set(ByVal Value As String)
                myName = Value
            End Set
        End Property

        Public Property Price() As Double
            Get
                Return myPrice
            End Get
            Set(ByVal Value As Double)
                myPrice = Value
            End Set
        End Property

    End Class

#End Region

End Namespace

Namespace CollectionClasses

#Region " Item Collection "

    'Create a Collection for each Object
    Public Class ItemCollection
        'Inherit from CollectionBase and expose the minimum
        'properties and methods necessary for complex databinding.
        'Every strongly typed collection should contain each of
        'the following members adjusted only for the data type
        'of the object being stored in the collection.
        Inherits System.Collections.CollectionBase

        Public Sub New()
            'The object MyBase refers to the object this class
            'inherits from.  Call the CollectionBase.New() method
            'in this class's New() method.
            MyBase.New()
        End Sub

        Default Public Overloads Property Item(ByVal index As Integer) As ObjectClasses.ItemObject
            Get
                Return MyBase.List.Item(index)
            End Get
            Set(ByVal Value As ObjectClasses.ItemObject)
                MyBase.List.Item(index) = Value
            End Set
        End Property

        'Multiple overloads of the Item property is not required. 
        'Providing an integer index to the List.Item() property
        'is all that Complex Databinding requires. This
        'overload allows the calling class to access Item() by
        'the Name() property of the ItemObject.
        Default Public Overloads Property Item(ByVal Name As String) As ObjectClasses.ItemObject
            Get
                Dim enm As IEnumerator = MyBase.List.GetEnumerator
                While enm.MoveNext
                    If CType(enm.Current, MultidimensionalCollectionExample.ObjectClasses.ItemObject).Name = Name Then
                        Return CType(enm.Current, MultidimensionalCollectionExample.ObjectClasses.ItemObject)
                    End If
                End While
                Return Nothing
            End Get
            Set(ByVal Value As ObjectClasses.ItemObject)
                For Each io As MultidimensionalCollectionExample.ObjectClasses.ItemObject In MyBase.List
                    If io.Name = Name Then
                        io = Value
                        Exit For
                    End If
                Next
            End Set
        End Property

        Public Function Add(ByVal value As ObjectClasses.ItemObject) As Integer
            Return MyBase.List.Add(value)
        End Function

        Public Sub AddRange(ByVal value As ItemCollection)
            MyBase.InnerList.AddRange(value)
        End Sub

        Public Function Contains(ByVal value As ObjectClasses.ItemObject) As Boolean
            Return MyBase.List.Contains(value)
        End Function

        Public Function IndexOf(ByVal value As ObjectClasses.ItemObject) As Integer
            Return MyBase.List.IndexOf(value)
        End Function

        Public Sub Insert(ByVal index As Integer, ByVal value As ObjectClasses.ItemObject)
            MyBase.List.Insert(index, value)
        End Sub

        Public Sub Remove(ByVal value As ObjectClasses.ItemObject)
            MyBase.List.Remove(value)
        End Sub
    End Class

#End Region

#Region " Order Collection "

    Public Class OrderCollection
        Inherits System.Collections.CollectionBase

        Public Sub New()
            MyBase.New()
        End Sub

        Default Public Property Item(ByVal index As Integer) As ObjectClasses.OrderObject
            Get
                Return MyBase.List.Item(index)
            End Get
            Set(ByVal Value As ObjectClasses.OrderObject)
                MyBase.List.Item(index) = Value
            End Set
        End Property

        Public Function Add(ByVal value As ObjectClasses.OrderObject) As Integer
            Return MyBase.List.Add(value)
        End Function

        Public Sub AddRange(ByVal value As ItemCollection)
            MyBase.InnerList.AddRange(value)
        End Sub

        Public Function Contains(ByVal value As ObjectClasses.OrderObject) As Boolean
            Return MyBase.List.Contains(value)
        End Function

        Public Function IndexOf(ByVal value As ObjectClasses.OrderObject) As Integer
            Return MyBase.List.IndexOf(value)
        End Function

        Public Sub Insert(ByVal index As Integer, ByVal value As ObjectClasses.OrderObject)
            MyBase.List.Insert(index, value)
        End Sub

        Public Sub Remove(ByVal value As ObjectClasses.OrderObject)
            MyBase.List.Remove(value)
        End Sub
    End Class

#End Region

#Region " Customer Collection "

    Public Class CustomerCollection
        Inherits System.Collections.CollectionBase

        'A class that inherits from another class can provide
        'it's own properties and methods in addition to
        'overloading or overriding base ones.
        Private myName As String = "New Collection"
        Private myDate As Date = Now

        Public Sub New()
            MyBase.New()
        End Sub

        Public Sub New(ByVal Name As String)
            myName = Name
        End Sub

        Public Sub New(ByVal [Date] As Date)
            myDate = [Date]
        End Sub

        Public Sub New(ByVal Name As String, ByVal [Date] As Date)
            myName = Name
            myDate = [Date]
        End Sub

        Default Public Overloads Property Item(ByVal index As Integer) As ObjectClasses.CustomerObject
            Get
                Return MyBase.List.Item(index)
            End Get
            Set(ByVal Value As ObjectClasses.CustomerObject)
                MyBase.List.Item(index) = Value
            End Set
        End Property

        Default Public Overloads Property Item(ByVal Name As String) As ObjectClasses.CustomerObject
            Get
                Dim enm As IEnumerator = MyBase.List.GetEnumerator
                While enm.MoveNext
                    If CType(enm.Current, MultidimensionalCollectionExample.ObjectClasses.CustomerObject).Name = Name Then
                        Return CType(enm.Current, MultidimensionalCollectionExample.ObjectClasses.CustomerObject)
                    End If
                End While
                Return Nothing
            End Get
            Set(ByVal Value As MultidimensionalCollectionExample.ObjectClasses.CustomerObject)
                For Each co As ObjectClasses.CustomerObject In MyBase.List
                    If co.Name = Name Then co = Value
                    Exit For
                Next
            End Set
        End Property

        Public Function Add(ByVal value As ObjectClasses.CustomerObject) As Integer
            Return MyBase.List.Add(value)
        End Function

        Public Sub AddRange(ByVal value As ItemCollection)
            MyBase.InnerList.AddRange(value)
        End Sub

        Public Function Contains(ByVal value As ObjectClasses.CustomerObject) As Boolean
            Return MyBase.List.Contains(value)
        End Function

        Public Function IndexOf(ByVal value As ObjectClasses.CustomerObject) As Integer
            Return MyBase.List.IndexOf(value)
        End Function

        Public Sub Insert(ByVal index As Integer, ByVal value As ObjectClasses.CustomerObject)
            MyBase.List.Insert(index, value)
        End Sub

        Public Sub Remove(ByVal value As ObjectClasses.CustomerObject)
            MyBase.List.Remove(value)
        End Sub

        Public Property CollectionName() As String
            Get
                Return myName
            End Get
            Set(ByVal Value As String)
                myName = Value
            End Set
        End Property

        Public Property CollectionDate() As String
            Get
                Return myDate
            End Get
            Set(ByVal Value As String)
                myDate = Value
            End Set
        End Property

    End Class

#End Region

End Namespace


Rember that this is a basic intro to custom collections.  As noted in the code comments, you can do more work yet to further exploit databinding.

If you look at the Order Collection class you get a basic template for a strongly-typed collection.  You can take that class and just replace instances of OrderObject with whatever object type you wish to make a collection for.

Sorry this is so much to look at... hope it helps though!
MigrationUser 1  Thursday, May 12, 2005 12:55 PM

You can use google to search for other answers

Custom Search

More Threads

• Unbound DataGridView not refreshing
• DataGridView.RowValidated event is thrown even if no edition occured.
• Simple Populate combobox from SQL server
• Not able to change row color based on the type of data
• Binding ComboBox DateTime
• Datagridview more than one vertical scroll bar appears
• Datagridview combobox column
• DataGridView Custom BackGround
• Capture data from datagridview to display into a texbox with a click of a button
• Bind Data Grid To Collection