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.

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;
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

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.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
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
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());
        }
    }
}

Compiling the Code

This example requires:

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

See Also

Concepts

Data Binding and Windows Forms

Reference

ITypedList

BindingList<T>

IBindingList