How to: Implement the ITypedList Interface

Implement the ITypedList interface to enable discovery of the schema for a bindable list.

Example

The following code example demonstrates how to implement the ITypedList interface. A generic type named SortableBindingList derives from the BindingList<T> class and implements the ITypedList interface. A simple class named Customer provides data, which is bound to the header of a DataGridView control.

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Collections;
using System.Reflection;

namespace ITypedListCS
{
    [Serializable()]
    public class SortableBindingList<T> : BindingList<T>, ITypedList
    {
        [NonSerialized()]
        private PropertyDescriptorCollection properties;

        public SortableBindingList() : base()
        {
            // Get the 'shape' of the list. 
            // Only get the public properties marked with Browsable = true.
            PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(
                typeof(T), 
                new Attribute[] { new BrowsableAttribute(true) });

            // Sort the properties.
            properties = pdc.Sort();
        }

        #region ITypedList Implementation

        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            PropertyDescriptorCollection pdc;

            if (listAccessors!=null && listAccessors.Length>0)
            {
                // Return child list shape.
                pdc = ListBindingHelper.GetListItemProperties(listAccessors[0].PropertyType);
            }
            else
            {
                // Return properties in sort order.
                pdc = properties;
            }

            return pdc;
        }

        // This method is only used in the design-time framework 
        // and by the obsolete DataGrid control.
        public string GetListName(PropertyDescriptor[] listAccessors)
        {   
            return typeof(T).Name;
        }

        #endregion
    }
}
Imports System.ComponentModel
Imports System.Collections.Generic
Imports System.Windows.Forms

<Serializable()> _
Public Class SortableBindingList(Of Tkey)
    Inherits BindingList(Of Tkey)
    Implements ITypedList

    <NonSerialized()> _
    Private properties As PropertyDescriptorCollection

    Public Sub New()
        MyBase.New()

        ' Get the 'shape' of the list. 
        ' Only get the public properties marked with Browsable = true.
        Dim pdc As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(Tkey), New Attribute() {New BrowsableAttribute(True)})

        ' Sort the properties.
        properties = pdc.Sort()

    End Sub

#Region "ITypedList Implementation"

    Public Function GetItemProperties(ByVal listAccessors() As System.ComponentModel.PropertyDescriptor) As System.ComponentModel.PropertyDescriptorCollection Implements System.ComponentModel.ITypedList.GetItemProperties

        Dim pdc As PropertyDescriptorCollection

        If (Not (listAccessors Is Nothing)) And (listAccessors.Length > 0) Then
            ' Return child list shape
            pdc = ListBindingHelper.GetListItemProperties(listAccessors(0).PropertyType)
        Else
            ' Return properties in sort order
            pdc = properties
        End If

        Return pdc

    End Function

    ' This method is only used in the design-time framework 
    ' and by the obsolete DataGrid control.
    Public Function GetListName( _
    ByVal listAccessors() As PropertyDescriptor) As String _
    Implements System.ComponentModel.ITypedList.GetListName

        Return GetType(Tkey).Name

    End Function

#End Region

End Class
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace ITypedListCS
{
	class Customer : INotifyPropertyChanged
    {
        public Customer() {}

        public Customer(int id, string name, string company, string address, string city, string state, string zip)
        {
            this._id = id;
            this._name = name;
            this._company = company;
            this._address = address;
            this._city = city;
            this._state = state;
            this._zip = zip;
        }

        #region Public Properties

        private int _id;

		public int ID
		{
			get { return _id; }
			set
			{
                if (_id != value)
                {
                    _id = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("ID"));
                }
			}
		}

		private string _name;

		public string Name
		{
			get { return _name; }
			set
			{
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Name"));
                }
			}
		}

		private string _company;

		public string Company
		{
			get { return _company; }
			set
			{
                if (_company != value)
                {
                    _company = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Company"));
                }
			}
		}

		private string _address;

		public string Address
		{
			get { return _address; }
			set
			{
                if (_address != value)
                {
                    _address = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Address"));
                }
			}
		}

		private string _city;

		public string City
		{
			get { return _city; }
			set
			{
                if (_city != value)
                {
                    _city = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("City"));
                }
			}
		}

		private string _state;

		public string State
		{
			get { return _state; }
			set
			{
                if (_state != value)
                {
                    _state = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("State"));
                }
			}
		}

		private string _zip;

		public string ZipCode
		{
			get { return _zip; }
			set
			{
                if (_zip != value)
                {
                    _zip = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("ZipCode"));
                }
			}
        }

        #endregion

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
		{
			if (null != PropertyChanged)
			{
				PropertyChanged(this, e);
			}
		}

		#endregion
	}
}
Imports System.ComponentModel

Public Class Customer
    Implements INotifyPropertyChanged

    Public Sub New()

    End Sub

    Public Sub New(ByVal id As Integer, ByVal name As String, ByVal company As String, ByVal address As String, ByVal city As String, ByVal state As String, ByVal zip As String)
        Me._id = id
        Me._name = name
        Me._company = company
        Me._address = address
        Me._city = city
        Me._state = state
        Me._zip = zip

    End Sub

#Region "Public Properties"

    Private _id As Integer
    Public Property ID() As Integer
        Get
            Return _id
        End Get
        Set(ByVal value As Integer)
            If _id <> value Then
                _id = value
                OnPropertyChanged(New PropertyChangedEventArgs("ID"))
            End If
        End Set
    End Property

    Private _name As String

    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            If _name <> value Then
                _name = value
                OnPropertyChanged(New PropertyChangedEventArgs("Name"))
            End If
        End Set
    End Property

    Private _company As String

    Public Property Company() As String
        Get
            Return _company
        End Get
        Set(ByVal value As String)
            If _company <> value Then
                _company = value
                OnPropertyChanged(New PropertyChangedEventArgs("Company"))
            End If
        End Set
    End Property


    Private _address As String

    Public Property Address() As String
        Get
            Return _address
        End Get
        Set(ByVal value As String)
            If _address <> value Then
                _address = value
                OnPropertyChanged(New PropertyChangedEventArgs("Address"))
            End If
        End Set
    End Property


    Private _city As String

    Public Property City() As String
        Get
            Return _city
        End Get
        Set(ByVal value As String)
            If _city <> value Then
                _city = value
                OnPropertyChanged(New PropertyChangedEventArgs("City"))
            End If
        End Set
    End Property


    Private _state As String

    Public Property State() As String
        Get
            Return _state
        End Get
        Set(ByVal value As String)
            If _state <> value Then
                _state = value
                OnPropertyChanged(New PropertyChangedEventArgs("State"))
            End If
        End Set
    End Property

    Private _zip As String

    Public Property ZipCode() As String
        Get
            Return _zip
        End Get
        Set(ByVal value As String)
            If _zip <> value Then
                _zip = value
                OnPropertyChanged(New PropertyChangedEventArgs("ZipCode"))
            End If
        End Set
    End Property

#End Region


#Region "INotifyPropertyChanged Members"

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Protected Overridable Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
        RaiseEvent PropertyChanged(Me, e)
    End Sub

#End Region

End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ITypedListCS
{
    public partial class Form1 : Form
    {
        private SortableBindingList<Customer> sortableBindingListOfCustomers;
        private BindingList<Customer> bindingListOfCustomers;

        private System.ComponentModel.IContainer components = null;
        private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
        private System.Windows.Forms.Label label2;
        private DataGridView dataGridView1;
        private Button button1;
        private Button button2;
        

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.sortableBindingListOfCustomers = new SortableBindingList<Customer>();
            this.bindingListOfCustomers = new BindingList<Customer>();

            this.dataGridView1.DataSource = this.bindingListOfCustomers;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.dataGridView1.DataSource = null;
            this.dataGridView1.DataSource = this.sortableBindingListOfCustomers;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.dataGridView1.DataSource = null;
            this.dataGridView1.DataSource = this.bindingListOfCustomers;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
            this.label2 = new System.Windows.Forms.Label();
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.flowLayoutPanel1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            this.SuspendLayout();
            // 
            // flowLayoutPanel1
            // 
            this.flowLayoutPanel1.AutoSize = true;
            this.flowLayoutPanel1.Controls.Add(this.label2);
            this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
            this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.flowLayoutPanel1.Name = "flowLayoutPanel1";
            this.flowLayoutPanel1.Size = new System.Drawing.Size(566, 51);
            this.flowLayoutPanel1.TabIndex = 13;
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(3, 6);
            this.label2.Margin = new System.Windows.Forms.Padding(3, 6, 3, 6);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(558, 39);
            this.label2.TabIndex = 0;
            this.label2.Text = "This sample demonstrates how to implement the ITypedList interface.  Clicking on the 'Sort Columns' button will bind the DataGridView to a sub-classed BindingList<T> that implements ITypedList to provide a sorted list of columns.  Clicking on the 'Reset' button will bind the DataGridView to a normal BindingList<T>.";
            // 
            // dataGridView1
            // 
            this.dataGridView1.AllowUserToAddRows = false;
            this.dataGridView1.AllowUserToDeleteRows = false;
            this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
            this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.Location = new System.Drawing.Point(6, 57);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.ReadOnly = true;
            this.dataGridView1.RowHeadersVisible = false;
            this.dataGridView1.Size = new System.Drawing.Size(465, 51);
            this.dataGridView1.TabIndex = 14;
            // 
            // button1
            // 
            this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.button1.Location = new System.Drawing.Point(477, 57);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(82, 23);
            this.button1.TabIndex = 15;
            this.button1.Text = "Sort Columns";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.button2.Location = new System.Drawing.Point(477, 86);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(82, 23);
            this.button2.TabIndex = 16;
            this.button2.Text = "Reset";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(566, 120);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.dataGridView1);
            this.Controls.Add(this.flowLayoutPanel1);
            this.Name = "Form1";
            this.Text = "ITypedList Sample";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.flowLayoutPanel1.ResumeLayout(false);
            this.flowLayoutPanel1.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        #endregion
    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
Imports System.ComponentModel
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form

    Friend WithEvents flowLayoutPanel1 As FlowLayoutPanel
    Friend WithEvents label2 As System.Windows.Forms.Label
    Friend WithEvents dataGridView1 As DataGridView
    Friend WithEvents button1 As Button
    Friend WithEvents button2 As Button

    Dim sortableBindingListOfCustomers As SortableBindingList(Of Customer)
    Dim bindingListOfCustomers As BindingList(Of Customer)

    Public Sub New()
        MyBase.New()

        Me.InitializeComponent()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        sortableBindingListOfCustomers = New SortableBindingList(Of Customer)()
        bindingListOfCustomers = New BindingList(Of Customer)()

        Me.dataGridView1.DataSource = bindingListOfCustomers
    End Sub


    Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click
        Me.dataGridView1.DataSource = Nothing
        Me.dataGridView1.DataSource = sortableBindingListOfCustomers
    End Sub


    Private Sub button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button2.Click
        Me.dataGridView1.DataSource = Nothing
        Me.dataGridView1.DataSource = bindingListOfCustomers
    End Sub

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(Form1))
        Me.flowLayoutPanel1 = New System.Windows.Forms.FlowLayoutPanel
        Me.label2 = New System.Windows.Forms.Label
        Me.dataGridView1 = New System.Windows.Forms.DataGridView
        Me.button1 = New System.Windows.Forms.Button
        Me.button2 = New System.Windows.Forms.Button
        Me.flowLayoutPanel1.SuspendLayout()
        CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.SuspendLayout()
        '
        'flowLayoutPanel1
        '
        Me.flowLayoutPanel1.AutoSize = True
        Me.flowLayoutPanel1.Controls.Add(Me.label2)
        Me.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top
        Me.flowLayoutPanel1.Location = New System.Drawing.Point(0, 0)
        Me.flowLayoutPanel1.Name = "flowLayoutPanel1"
        Me.flowLayoutPanel1.Size = New System.Drawing.Size(566, 51)
        Me.flowLayoutPanel1.TabIndex = 13
        '
        'label2
        '
        Me.label2.AutoSize = True
        Me.label2.Location = New System.Drawing.Point(3, 6)
        Me.label2.Margin = New System.Windows.Forms.Padding(3, 6, 3, 6)
        Me.label2.Name = "label2"
        Me.label2.Size = New System.Drawing.Size(558, 39)
        Me.label2.TabIndex = 0
        Me.label2.Text = "This sample demonstrates how to implement the ITypedList interface.  Clicking on the 'Sort Columns' button will bind the DataGridView to a sub-classed BindingList<T> that implements ITypedList to provide a sorted list of columns.  Clicking on the 'Reset' button will bind the DataGridView to a normal BindingList<T>."
        '
        'dataGridView1
        '
        Me.dataGridView1.AllowUserToAddRows = False
        Me.dataGridView1.AllowUserToDeleteRows = False
        Me.dataGridView1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill
        Me.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
        Me.dataGridView1.Location = New System.Drawing.Point(6, 57)
        Me.dataGridView1.Name = "dataGridView1"
        Me.dataGridView1.ReadOnly = True
        Me.dataGridView1.RowHeadersVisible = False
        Me.dataGridView1.Size = New System.Drawing.Size(465, 51)
        Me.dataGridView1.TabIndex = 14
        '
        'button1
        '
        Me.button1.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.button1.Location = New System.Drawing.Point(477, 57)
        Me.button1.Name = "button1"
        Me.button1.Size = New System.Drawing.Size(82, 23)
        Me.button1.TabIndex = 15
        Me.button1.Text = "Sort Columns"
        Me.button1.UseVisualStyleBackColor = True
        '
        'button2
        '
        Me.button2.Location = New System.Drawing.Point(477, 86)
        Me.button2.Name = "button2"
        Me.button2.Size = New System.Drawing.Size(82, 23)
        Me.button2.TabIndex = 16
        Me.button2.Text = "Reset"
        Me.button2.UseVisualStyleBackColor = True
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(566, 120)
        Me.Controls.Add(Me.button2)
        Me.Controls.Add(Me.button1)
        Me.Controls.Add(Me.dataGridView1)
        Me.Controls.Add(Me.flowLayoutPanel1)
        Me.Name = "Form1"
        Me.Text = "ITypedList Sample"
        Me.flowLayoutPanel1.ResumeLayout(False)
        Me.flowLayoutPanel1.PerformLayout()
        CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
        Me.ResumeLayout(False)
        Me.PerformLayout()
    End Sub

    Shared Sub Main()
        Application.Run(New Form1())
    End Sub

End Class


Compiling the Code

This example requires:

  • References to the System.Drawing and System.Windows.Forms assemblies.

See Also

ITypedList
BindingList<T>
IBindingList
Data Binding and Windows Forms