Question Apply databinding to inherited control

hermiod

Member
Joined
Mar 17, 2008
Messages
6
Programming Experience
1-3
I'm trying to modify the listview to have similar databinding behaviour to a datagrid with the added bonus of having the ability to group by a column in the datasource.

Below is the code I have developed so far. The only problem is that this code only binds to a datatable. I'd like to be able to bind to all the sources that a datagrid can (datatable, collection, etc).
I've spent alot of time searching on the net and all the examples I have found either show what I have already done, implement much more complex databinding than I need, or I can't make enough sense of the code to be able to modify it to suits my needs!

Could anyone point me to somewhere that will show me how to do what I need or give me a few pointers

VB.NET:
Imports System.ComponentModel

Public Class GroupingListView
    Inherits System.Windows.Forms.ListView

    Private _dataSource As Object
    Private _GroupBy As String
    Private _GroupTitle As String
    Private _GroupTag As String
    Private DoGrouping As Boolean = False
    Private DoGroupTitle As Boolean = False
    Private DoGroupTag As Boolean = False

    <TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")> _
    <Category("Data")> _
    Public Property DataSource() As Object
        Get
            Return _dataSource
        End Get
        Set(ByVal value As Object)
            _dataSource = value
        End Set
    End Property

    <Category("Data")> _
    Public Property GroupBy() As String
        Get
            Return _GroupBy
        End Get
        Set(ByVal value As String)
            _GroupBy = value
        End Set
    End Property

    <Category("Data")> _
    Public Property GroupTitle() As String
        Get
            Return _GroupTitle
        End Get
        Set(ByVal value As String)
            _GroupTitle = value
        End Set
    End Property

    <Category("Data")> _
    Public Property GroupTag() As String
        Get
            Return _GroupTag
        End Get
        Set(ByVal value As String)
            _GroupTag = value
        End Set
    End Property

    Public Sub DataBind()
        If _dataSource Is Nothing Then
            Console.WriteLine("No datasource to bind to")
            Exit Sub
        End If

        If TypeOf _dataSource Is DataSet Then
            If CType(_dataSource, DataSet).Tables.Count > 0 Then
                _dataSource = CType(_dataSource, DataSet).Tables(0)
            End If
        End If

        Dim lvwItem As New ListViewItem
        Dim lvwGroup As New ListViewGroup
        Dim Data As DataTable = CType(_dataSource, DataTable)
        Dim NoColumns As Integer = Data.Columns.Count

        For Each dc As DataColumn In Data.Columns
            Me.Columns.Add(dc.ColumnName, 100)

            If _GroupBy = dc.ColumnName Then
                DoGrouping = True
            End If
        Next

        For Each dc As DataColumn In Data.Columns
            If _GroupTag = dc.ColumnName Then
                'MsgBox("Column: " & dc.ColumnName & vbNewLine & "groupby: " & _GroupBy)
                DoGroupTag = True
            End If
        Next

        For Each dc As DataColumn In Data.Columns
            If _GroupTitle = dc.ColumnName Then
                DoGroupTitle = True
            End If
        Next

        If DoGrouping = True Then
            For Each dr As DataRow In Data.Rows
                If lvwGroup.Name <> dr.Item(_GroupBy) Then
                    lvwGroup = New ListViewGroup()
                    lvwGroup.Name = dr.Item(_GroupBy)

                    If DoGroupTitle = True Then
                        lvwGroup.Header = dr.Item(_GroupTitle)
                    Else
                        lvwGroup.Header = dr.Item(_GroupBy)
                    End If

                    'If GroupTag property is a column that exists in the column 
                    'then assign the property of this column to the tag property.
                    If DoGroupTag = True Then
                        lvwGroup.Tag = dr.Item(_GroupTag)
                    End If

                    Me.Groups.Add(lvwGroup)

                    lvwItem = New ListViewItem
                    lvwItem.Text = dr.Item(0)
                    For i As Integer = 1 To NoColumns - 1
                        lvwItem.SubItems.Add(dr.Item(i))
                    Next
                    Me.Items.Add(lvwItem)
                    Me.Groups(lvwGroup.Name).Items.Add(lvwItem)
                Else
                    lvwItem = New ListViewItem
                    lvwItem.Text = dr.Item(0)
                    For i As Integer = 1 To NoColumns - 1
                        lvwItem.SubItems.Add(dr.Item(i))
                    Next
                    Me.Items.Add(lvwItem)
                    Me.Groups(lvwGroup.Name).Items.Add(lvwItem)
                End If
            Next
        Else
            For Each dr As DataRow In Data.Rows
                lvwItem = New ListViewItem
                lvwItem.Text = dr.Item(0)
                For i As Integer = 1 To NoColumns - 1
                    lvwItem.SubItems.Add(dr.Item(i))
                Next
                Me.Items.Add(lvwItem)
            Next
        End If

        AutoSizeColumns()

    End Sub

End Class
 
Done quick and dirty, but it seems to work with all my small tests in the very short time this was done (about 1 hour or so)... see attached project. As for pointers, it is done with Reflection, it's the only way to iterate the properties of unknown objects. I removed GroupTitle property because I couldn't figure out what you meant with it, but added the standard DataMember property to choose between tables in a Dataset. Have a look.
 

Attachments

  • vbnet20-ListViewBinding.zip
    15.8 KB · Views: 30
Thanks JohnH, that is absolutely brilliant! I've only had a quick look but it seems that will do exactly what I need.

As for GroupTitle, it is intended for use where you might want the title of the group to be different from the values in the GroupBy field, in my case I want to group by a course code but want the title to show the course description as there is the possibility that two different courses could have the same description, which will bugger up the grouping.

Thanks again for that JohnH, much appreciated.
 
Some more info, except handling IList types specifically (that includes arrays and collections) I just checked for Dataset/Datatable types. There is a common denominator for these also, they both implement IListSource. This is an interface with only two members, GetList method that returns an IList of items (for example DataTable rows), and ContainsListCollection property that tell if the returned IList items themselves are IList instances (for example True for Dataset which may contain multiple tables). Also other data sets/table types may implement IListSource and be bindable, so to take this class further it could be an idea to look more into reflecting IListSource.
 
Back
Top