Click here to Skip to main content
16,018,394 members
Articles / Desktop Programming / Windows Forms
Article

Data binding a ListView

Rate me:
Please Sign up or sign in to vote.
4.60/5 (29 votes)
9 Apr 2005CPOL4 min read 538.8K   11.8K   112   19
An overview on inheriting a ListView and implementing design-time data binding capability.

Sample Image - ListView_DataBinding.jpg

Introduction

The ListView control provided by Microsoft does not allow design-time data binding. This article gives a basic overview to get you started in implementing this functionality. It will also show you how to hide the base class’ properties and to sort the currency manager when the list view is sorted.

Background

I am in the process of architecting a system under strict time constraints; hence I wanted to use Databinding for rapid development. However, I discovered that the ListView control cannot be bound to a DataSource at design-time. And just like most of you developers out there, after GOOGLING around I decided to post an article on this topic. What I am explaining in this article is quite basic, but it is meant to get you started. There are many modifications and changes that can be made to the source as it is not perfect. Hence, I will update the article as I add useful functionality to the control.

Disclaimer

Please note that the Visual Studio Generated Code is not listed below. You will find the code in its entirety in the source-code above. In addition, the code is not perfect, but it works. It is in bits-n-pieces to just get the point across. The code implemented for this article uses the Customer table from the NorthWind database in SQL Server 2000.

Getting Started

Before you run the code, make sure to change the ConnectionString property of the sqlConnection1 object to point to your database. You can modify the string below and replace the ConnectionString property in the designer.

sqlConnection1.ConnectionString = 
"workstation id=YOUR_WORKSTATION_ID;packet size=4096;" +
"integrated security=SSPI;data source=YOUR_SQL_SERVER;" + 
"persist security info=True;initial catalog=Northwind"

Once you replace the above string, you should be able to run the code snippet.

First and foremost, the required namespaces are listed below and also the fact that we inherit from the ListView control.

C#
using System;
using System.ComponentModel;
using System.Data;
using System.Windows.Forms;

public class MyListView : System.Windows.Forms.ListView

Class Variables

We define two class variables. A CurrencyManager cm to track the current selected record and a DataView dv to sort the columns on the CurrencyManager.

CurrencyManager <CODE>cm</CODE> = null;
DataView <CODE>dv</CODE> = null;

Constructor

The constructor is straight-forward. We add two event-handlers. One for the selected-index-changed event and the other for the column-header-click event.

C#
public MyListView()
{
    // This call is required by the Windows.Forms Form Designer.
    InitializeComponent();

    base.SelectedIndexChanged +=new EventHandler(
                       MyListView_SelectedIndexChanged);
    base.ColumnClick +=new ColumnClickEventHandler(
                       MyListView_ColumnClick);
}

Properties

We add the DataSource property as an Object type. Then, we set the Bindable attribute to true which forces the property grid to display this member under the DataBindings collection. The TypeConverter attribute is set to “System.Windows.Forms.Design.DataSourceConverter, System.Design”. Now, the DataSource property shows all the available data sources at design-time in the property-grid. Finally, the Category attribute categorizes the property under the Data Category.

C#
private Object source;

[Bindable(true)]
[TypeConverter("System.Windows.Forms.Design.DataSourceConverter, 
                                           System.Design")]
[Category("Data")]
public Object DataSource
{
    get
    {
        return source;
    }
    set
    {
        source = value;
    }
}

Next we add the DataMember property as a String type to store the Name of the field to be displayed in the ListView. Once again we set the Bindable attribute to true and Category to Data. Next, the Editor attribute is set to “System.Windows.Forms.Design.DataMemberFieldEditor, System.Design”, which shows us the columns associated with the DataSource. The RefreshProperties attribute is set to re-query all the properties and the refresh the view. Finally, on setting the DataMember property, we call the bind() method which takes care of populating the ListView.

C#
private String data;
[Bindable(true)]
[Category("Data")]
[Editor("System.Windows.Forms.Design.DataMemberFieldEditor, 
  System.Design", "System.Drawing.Design.UITypeEditor, 
  System.Drawing")]
[RefreshProperties(RefreshProperties.All)]
public String DataMember
{
    get
    {
        return data;
    }
    set
    {
        data = value;
        bind();
    }
}

Then, we hide the base class' Sorting property from the property grid by re-defining the Sorting property with the exact signature and setting its browsable attribute to false. This hides the Sorting property at design-time, but it is still accessible through the code. Please note: this property does not need to be hidden. You can write additional code at startup to sort the list based on the design-time selection.

C#
[Browsable(false)]
public new SortOrder Sorting
{
    get
    {
        return base.Sorting;
    }
    set
    {
        base.Sorting = value;
    }
}

Methods

Finally, this is where the rubber meets the road. The method bind() pulls out a DataRowCollection from the DataSource and populates the ListView.

C#
private void bind()
{
    //Clear the existing list
    Items.Clear();                                            
    //This implementation assumes the DataSource is a DataSet
    if(source is DataSet)                                        
    {
        DataSet ds = source as DataSet;
        DataTable dt = ds.Tables[0];

        if(dt!=null)
        {
             
            //get the Binding Context for the Form
            cm = (CurrencyManager)BindingContext[ds, 
                              ds.Tables[0].TableName];           
           
            //add an event handler for the Currency Manager       
            cm.CurrentChanged +=new EventHandler(cm_CurrentChanged);                
           
            //Get the Currency Manager Collection as a DataView
            dv = (DataView)cm.List;                                

            //Create the column header based on the DataMember
            Columns.Add(DataMember, ClientRectangle.Width - 17, 
                                      HorizontalAlignment.Left); 
             //Add each Row as a ListViewItem        
            foreach(DataRow dr in dt.Rows)                           
            {
                ListViewItem lvi = new ListViewItem(
                                        dr[DataMember].ToString());
                lvi.Tag = dr;
                Items.Add(lvi);
            }
            //Set the Sorting property as Ascending
            Sorting = SortOrder.Ascending;  
                                      
            //Sort the DataView on the DataMember          
            dv.Sort = this.Columns[0].Text + " ASC";                        
        }
    }
    else
    {
        //If no source is defined, Currency Manager is null  
        cm = null;                                        
    }
}

Events

There are three basic events that I have written for this control.

  1. Selected-Index-Changed event handler is in charge of updating the Position property on the Currency Manager, which forces other bound controls to update to the newly selected record.
    C#
    private void MyListView_SelectedIndexChanged(object sender, EventArgs e)
    {
        if(this.SelectedIndices.Count>0)
        {               
            if(cm!=null)
            {
                 cm.Position = base.SelectedIndices[0];
            }
        }
    }
  2. A Column-Click event handler to sort the ListView and the DataView in sync with the Currency Manager’s list.
    C#
    private void MyListView_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        if(Sorting==SortOrder.None || Sorting == SortOrder.Descending)
        {
            Sorting = SortOrder.Ascending;
            dv.Sort = this.Columns[0].Text + " ASC";
        }
        else if(Sorting==SortOrder.Ascending)
        {
            Sorting = SortOrder.Descending;
            dv.Sort = this.Columns[0].Text + " DESC";
        }
    }
  3. Currency Manager’s position change event handler selects the appropriate item in the ListView.
    C#
    private void cm_CurrentChanged(object sender, EventArgs e)
    {
        this.Items[cm.Position].Selected = true;
    }

Voila! We have now successfully implemented Data-Binding to a ListView control. The binding explained above is quite limited in its features, but you can further expand on the control to enhance the basic features.

Points of Interest

While writing this control, I discovered that a generic class can be written for binding custom controls. This would allow implementing binding on new controls really fast without much coding. I will submit an article about it when I get a chance.

History

  • April 2, 2005

    Submitted the original article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionBinding an Enum property values Pin
msanaei4-Oct-16 22:17
msanaei4-Oct-16 22:17 
GeneralMy Vote of 5 Pin
SRDev13-Aug-14 8:54
SRDev13-Aug-14 8:54 
Questioncan't download Pin
MyBirch12-Mar-14 20:20
MyBirch12-Mar-14 20:20 
AnswerRe: can't download Pin
theoutlander12-Mar-14 20:36
theoutlander12-Mar-14 20:36 
GeneralMy vote of 5 Pin
karthik reddy mereddy7-Aug-13 0:10
karthik reddy mereddy7-Aug-13 0:10 
GeneralMy vote of 1 Pin
arnavs7-Feb-13 19:07
arnavs7-Feb-13 19:07 
Questionpersist security info=True (not recommended) Pin
Jeffrey Walton6-Jan-12 10:15
Jeffrey Walton6-Jan-12 10:15 
persist security info={No|false} is recommended. See for example, SqlConnection.ConnectionString Property.
GeneralMy vote of 1 Pin
raju0608761-Jul-10 1:01
raju0608761-Jul-10 1:01 
GeneralRe: My vote of 1 PinPopular
jgauffin21-Aug-11 22:39
jgauffin21-Aug-11 22:39 
GeneralRe: My vote of 1 Pin
nadjibus10-Dec-14 8:10
nadjibus10-Dec-14 8:10 
GeneralSmall improvement Pin
Ernest Poletaev13-Jan-07 10:45
Ernest Poletaev13-Jan-07 10:45 
GeneralAdding Subitems Pin
dorrell9-Feb-06 10:09
dorrell9-Feb-06 10:09 
NewsRe: Adding Subitems Pin
theoutlander22-Feb-06 12:52
theoutlander22-Feb-06 12:52 
GeneralChanges to DataView Pin
danmccune26-May-05 10:09
danmccune26-May-05 10:09 
GeneralRe: Changes to DataView Pin
theoutlander26-May-05 19:39
theoutlander26-May-05 19:39 
GeneralBad link in code Pin
fwsouthern11-Apr-05 9:03
fwsouthern11-Apr-05 9:03 
Generalreally databound ListView Pin
dag dag7-Apr-05 23:02
dag dag7-Apr-05 23:02 
GeneralRe: really databound ListView Pin
Sergey.Volchkov9-Apr-05 10:52
sussSergey.Volchkov9-Apr-05 10:52 
GeneralRe: really databound ListView Pin
joehit1-Jul-10 11:35
joehit1-Jul-10 11:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.