Data Binding

Better ListView supports both lookup binding and complex binding of user-provided data.

Complex binding allows you use any list-type collection (implementing IList or IListSource) to the control. Complex binding is not a feature of all WinForms controls.

Regular .NET ListView does not support binding of lists.

Lookup binding is supported on any WinForms control automatically, so we won't cover the topic here.

Basic Data Binding

To bind a custom IList instance, set the DataSource property:

C#

listView.DataSource = myList;  // myList is of type List<Person>

Visual Basic

ListView.DataSource = myList  ' myList is of type List(Of Person)()

This fills Better ListView with objects in myList:

When converting Person objects to items, Better ListView first looks if there is a TypeConverter defined to use ConvertToString method. If not, standard ToString method is called to obtain item text.

Displaying Custom Properties

To display value of some specific property of the bound object, use the DisplayMember property. For example, if we set the DisplayMember to "Age", Better ListView will represent each Person object by its Age property:

The DisplayMember property can be set on columns as well, so that each column can display another property of the bound object.

Working with Values instead of Items

Each column in Better ListView can represent different property of the bound object. The property can be specified using ValueMember property (also on each column using BetterListViewColumnHeader.ValueMember). When specified, the property value can be accessed using the BetterListViewItem.Value (BetterListViewSubItem.Value).

For example, if we set ValueMember to "Age", each item will have the Value property set to corresponding short with the person's age.

You can also use following properties to work with selection in terms of the bound objects (values):

Binding Columns

It is possible to display object properties in columns simply by setting DataBindColumns to true. This will cause Better ListView to generate column for each public property of provided list item type automatically:

Here the List<Person> object is bound. The Person type contains three public properties:

Column header texts are generated from property names. DisplayNameAttribute can be used on the property to specify custom name (as used in the Age column).

Binding Position

Data binding mechanism in WinForms keeps information about current position in the bound list. Better ListView synchronizes current position with its selection.

To turn off this behvior, set DataBindPosition property to false. In this case, Better ListView selection will be independent on current position in the bound list.

Sorting Items

Item sorting can be a nontrivial update of the bound list (which is unsupported by the standard data binding mechanism).

SortVirtual property should be set to true to turn off physical update of the Items collection. The columns will still display sort glyphs and the SortList will contain new sort state information, so the manual sorting is possible.

For example, if we have Person[] array bound, we can sort it manually in the AfterItemSort event handler this way:

C#

// get values from the data source
Person[] values = (Person[])this.listView.DataSource;

// get listview items as keys
BetterListViewItem[] keys = new BetterListViewItem[values.Length];

this.listView.Items.CopyTo(keys, 0);

// create custom comparer
BetterListViewItemComparer comparer = this.listView.ItemComparer;

comparer.SetSortList(this.listView.SortList, this.listView.Columns, true);

// sort the data
Array.Sort(keys, values, comparer);

// refresh view
((CurrencyManager)this.listView.BindingContext[this.listView.DataSource]).Refresh();

Visual Basic

' get values from the data source
Dim values As Person() = DirectCast(Me.ListView.DataSource, Person())

' get listview items as keys
Dim keys As BetterListViewItem() = New BetterListViewItem(values.Length - 1) {}

Me.ListView.Items.CopyTo(keys, 0)

' create custom comparer
Dim comparer As BetterListViewItemComparer = Me.ListView.ItemComparer

comparer.SetSortList(Me.ListView.SortList, Me.ListView.Columns, True)

' sort the data
Array.Sort(keys, values, comparer)

' refresh view
DirectCast(Me.ListView.BindingContext(Me.ListView.DataSource), CurrencyManager).Refresh()

Sorting can also be achieved by using DataTable, DataView or other type that supports sorting while bound to a control (such types implement IBindingList) as a data source:

C#

// get data source
DataTable dataTable = (DataTable)this.listView.DataSource;

// set sort
dataTable.DefaultView.Sort = "Name ASC, Age DESC";

// refresh view
(((CurrencyManager)this.listView.BindingContext[this.listView.DataSource]).Refresh();

Visual Basic

' get data source
Dim dataTable As DataTable = DirectCast(Me.ListView.DataSource, DataTable)

' set sort
dataTable.DefaultView.Sort = "Name ASC, Age DESC"

' refresh view
DirectCast(Me.ListView.BindingContext(Me.ListView.DataSource), CurrencyManager).Refresh()

Sorting by Value

Items can be sorted by other than displayed value when ValueMember property is set.

For example, we have a DataTable with columns "PercentDone" - which contains numeric values - and "PercentDoneDisplay" which contains corresponding values for display (e.g. rounded, with percent sign). Setting DisplayMember property on the column for percentage to "PercentDoneDisplay" and ValueMember property to "PercentDone" causes sorting according to value in numeric column.

Values are used for sorting only when the Key property of a sub-item is not available. The Key property has the highest priority when sorting, then the Value property, and then the Text property.

Following image shows multi-column sorting of a bound DataTable - the table is sorted according to column with aspect ratio enumeration (invisible, showing another column with display values) and a numeric column (percentage):

Reordering Items and Columns

When some data is bound to Better ListView and columns are reordered, the control automatically performs refresh of the data (this is the case of ColumnReorderMode set to Enabled).

Automatic item reordering is restricted to happen on the same item level or between different levels, but only when none of the levels are the top level.

Item reordering with data binding should be implemented in the similar manner as item sorting. First, set the ItemReorderMode property to Custom. Then implement the custom reordering logic on data source in the AfterItemReorder event handler.

Sample Source Code

The following sample will binds a list of Person objects to Better ListView:

C#

// create a list of Person objects
List&lt;Person&gt; persons = new List&lt;Person&gt;(new[]
                                        {
                                            new Person("Lee Adama", 45),
                                            new Person("Sally Gordon", 26),
                                            new Person("John Grant", 18),
                                            new Person("Susan Hutchinson", 37)
                                        });

// create columns automatically
this.listView.DataBindColumns = true;

// populate ListView with our data
this.listView.DataSource = persons;

Visual Basic

' create a list of Person objects
Dim persons As New List(Of Person)(
    New Person() { _
                     New Person("Lee Adama", 45),
                     New Person("Sally Gordon", 26),
                     New Person("John Grant", 18),
                     New Person("Susan Hutchinson", 37)
                 })

' create columns automatically
ListView.DataBindColumns = True

' populate ListView with our data
ListView.DataSource = persons

The Person class itself is particularly simple - it does not need to provide anything else than public properties which are bound:

C#

/// <summary>
///   Represents a simple data object (a person).
/// </summary>
internal sealed class Person
{
    /// <summary>
    ///   Gets or sets the name of the person.
    /// </summary>
    /// <value>
    ///   The name of the person.
    /// </value>
    public string Name
    {
        get;
        set;
    }

    /// <summary>
    ///   Gets or sets the age of the person.
    /// </summary>
    /// <value>
    ///   The age of the person.
    /// </value>
    public int Age
    {
        get;
        set;
    }

    /// <summary>
    ///   Initializes a new instance of the <see cref = "Person" /> class.
    /// </summary>
    /// <param name = "name">The name of the person.</param>
    /// <param name = "age">The age of the person.</param>
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Visual Basic

''' <summary>
'''   Represents a simple data object (a person).
''' </summary>
Friend NotInheritable Class Person

    ''' <summary>
    '''   Gets or sets the name of the person.
    ''' </summary>
    ''' <value>
    '''   The name of the person.
    ''' </value>
    Public Property Name() As String
        Get
            Return _mName
        End Get
        Set(ByVal value As String)
            _mName = value
        End Set
    End Property

    ''' <summary>
    '''   Gets or sets the age of the person.
    ''' </summary>
    ''' <value>
    '''   The age of the person.
    ''' </value>
    Public Property Age() As Integer
        Get
            Return _mAge
        End Get
        Set(ByVal value As Integer)
            _mAge = value
        End Set
    End Property

    Private _mName As String
    Private _mAge As Integer

    ''' <summary>
    '''   Initializes a new instance of the <see cref = "Person" /> class.
    ''' </summary>
    ''' <param name = "Name">The name of the person.</param>
    ''' <param name = "Age">The age of the person.</param>
    Public Sub New(ByVal Name As String, ByVal Age As Integer)
        Me.Name = Name
        Me.Age = Age
    End Sub

End Class