Home  |  FAQ  |  About  |  Contact  |  View Source   
 
SEARCH:
 
BROWSE:
    My Hood
Edit My Info
View Events
Read Tutorials
Training Modules
View Presentations
Download Tools
Scan News
Get Jobs
Message Forums
School Forums
Member Directory
   
CONTRIBUTE:
    Sign me up!
Post an Event
Submit Tutorials
Upload Tools
Link News
Post Jobs
   
   
Home >  Tutorials >  C# >  Creating and Sorting the ListView
Add to MyHood
   Creating and Sorting the ListView   [ printer friendly ]
Stats
  Rating: 4.35 out of 5 by 23 users
  Submitted: 03/04/02
Victor Vuong ()

 
After spending a few frustrated hours on multiple column sorting for ListViews because I couldn’t find a single good tutorial, I decided to write my own tutorial talking about how ListViews work and how to sort multiple columns. It’s 11:48 pm, so let’s get cracking before I get too sleepy.

I am using Visual Studio.NET to do the programming and will try to adapt pure code instead of using the pre-generated code done by Visual Studio. I assume that you have already generated a basic C# Windows Application project so I will just write new functions and variables that will create the ListView.

In this tutorial we are going to create a ListView and have 3 columns for First Name, Last Name, Age. We will have functions to add and sort the columns.

We start off by declaring our basic variables.
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ColumnHeader columnFirstName;
private System.Windows.Forms.ColumnHeader columnLastName;
private System.Windows.Forms.ColumnHeader columnAge;

listView1 is going to be our main ListView and we are declaring the variables for the columns. We then move on to initializing the variables.
private void InitListView()
{
    // New up the variables
    listView1 = new ListView();
    columnFirstName = new ColumnHeader();
    columnLastName = new ColumnHeader();
    columnAge = new ColumnHeader();
    
    // Set position and size
    listView1.Bounds = new Rectangle(new Point(10,10), new Size(305,200));

    // Set the view to show details.
    listView1.View = View.Details;
    // Select the item and subitems when selection is made.
    listView1.FullRowSelect = true;
    // Set the default Sorting order
    listView1.Sorting = SortOrder.Ascending;
    // Display grid lines.
    listView1.GridLines = true;    

    // Set the text and width of the columns
    columnFirstName.Text = "First Name";
    columnFirstName.Width = 100;
    columnLastName.Text = "Last Name";
    columnLastName.Width = 100;
    columnAge.Text = "Age";
    columnAge.Width = 100;
    
    listView1.Columns.AddRange(new ColumnHeader[] { columnFirstName, columnLastName, columnAge });
    this.Controls.Add(listView1);}
The code above is fairly simple to understand. We first new up our variables and then we set the size and position of the ListView. The ListView has a few modes of viewing. Take a look at Windows Explorer and you can view the contents as small icons, list, details, etc. We are going to look at viewing things in Details mode. We also are going to set the full select to true, which means that when you select a row, the whole row will become highlighted. We will discuss about the sorting later. After filling out the list view information we fill out our column information by setting the Text and the width. We then add it to our ListView and add our ListView to the controls of the form.

We now move on to adding data to our simple ListView.
private void AddData(string fName, string lName, int Age)
{
    ListViewItem lvi;

    string[] aHeaders = new string[3];
    
    aHeaders[0] = fName;
    aHeaders[1] = lName;
    aHeaders[2] = Age.ToString();

    lvi = new ListViewItem(aHeaders);
    listView1.Items.Add(lvi);        
}
In this function we are accepting 3 parameters for the information to be filled out. We then create an array of strings of size 3 because one of the constructors of a ListViewItem takes a string[]. We fill it out accordingly and add it to the ListView. So far so good, so now we are going to be doing the fun stuff – sorting.

To start off on sorting I am going to explain a few parameters about the ListView class. In the ListView class it has a property called ListViewItemSorter which basically takes in an IComparer object and uses it to do all the comparisons. The good thing about this comparer object is that now you can monitor the columns being clicked and use your own sort methods on the ListView. So let’s go ahead and create this comparer object.
public class Sorter : IComparer
{
    // Initialize the variables to default
    public int column = 0;
    public bool bAscending = true;
                    
    // Using the Compare function of IComparer
    public int Compare(object x, object y)
    {
        // Cast the objects to ListViewItems
        ListViewItem lvi1 = (ListViewItem) x;
        ListViewItem lvi2 = (ListViewItem) y;

        // If the column is the string columns
        if(column != 2)
        {
            string lvi1String = lvi1.SubItems[column].ToString();
            string lvi2String = lvi2.SubItems[column].ToString();

            // Return the normal Compare
            if(bAscending)
                return String.Compare(lvi1String, lvi2String);
            
            // Return the negated Compare
            return -String.Compare(lvi1String, lvi2String);
        }
        
        // The column is the Age column
        int lvi1Int = ParseListItemString(lvi1.SubItems[column].ToString());
        int lvi2Int = ParseListItemString(lvi2.SubItems[column].ToString());

        // Return the normal compare.. if x < y then return -1
        if(bAscending)
        {
            if(lvi1Int < lvi2Int)
                return -1;
            else if(lvi1Int == lvi2Int)
                return 0;

            return 1;
        }

        // Return the opposites for descending
        if(lvi1Int > lvi2Int)
            return -1;
        else if(lvi1Int == lvi2Int)
            return 0;

        return 1;
    }

    private int ParseListItemString(string x)
    {
        //ListViewItems are returned like this: "ListViewSubItem: {19}"
        int counter = 0;
        for(int i = x.Length - 1; i >= 0; i--, counter++)
        {
            if(x[i] == '{')
                break;            
        }

        return Int32.Parse(x.Substring(x.Length - counter, counter-1));
    }
}
Wow, we’re almost done. So now we have created this Sorter object to pass into our ListView. With this object we can now check the column being clicked by setting the column of the class. Looking at the code you can see that we grab the actual item from the SubItems of the specified column and do our comparison on that. It will return a normal comparison of Object X < Object Y which is what the Compare function does. We also check to see if we want to do Ascending or Descending values because when you click a column, if it’s sorted Ascending, we now want it sorted Descending.

There is something funny about the code if you have noticed. I had to make a ParseListItemString function to take in a ListViewItem string and change it to an int. The thing about ListViewItems is they return a string like this: "ListViewSubItem: {19}" so you have to get the int from that string. I did a simple routine to find the ‘{‘ and get the integer.

So now lets make it sort when the column headers are clicked. In Windows Forms, when a column header is clicked it sends off a ColumnClickEventHandler. So now let’s bind the ColumnClickEventHandler to a function. To do this we are going to add the following line of code to our InitListView function.
listView1.ColumnClick += new ColumnClickEventHandler(listView1_ColumnClick);
We are then going to create the corresponding function that was added to this delegate.
private void listView1_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
{
    Sorter columnSorter = new Sorter();
    columnSorter.column = e.Column;
        
    if((columnSorter.bAscending = (listView1.Sorting == SortOrder.Ascending)))
        listView1.Sorting = SortOrder.Descending;
    else
        listView1.Sorting = SortOrder.Ascending;
    
    listView1.ListViewItemSorter = columnSorter;
}
Yay! So now we are all done. Let me finish up by explaining the click event. The way the tutorial does this is very simple. The ListView has a property called Sorting which takes in a SortOrder. Normally what this property does is that it allows you to choose if the items are going to be sorted Ascending or Descending for the Default sorter. But since we made our own IComparer function, the Sorting property does absolutely nothing. I decided to use the property to keep track of which way I last sorted. I could have also made a global Boolean to do this but I saved a variable and used the ListView’s property. So when I click on a column header, I check if my ListView is currently in Ascending sort. If so then put it to Descending sort.

Next, the ColumnClickEventArgs will give me the column that is being clicked. This is a wonderful tool because now I can pass in the column into my Sorter class and now that class knows what column to sort by. I then pass the Sorter class to the ListViewItemSorter property, and voila! I am done.

I made a very simple application that has a button that will create the ListView and add data to it. Here is the complete code:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;

public class Form1 : System.Windows.Forms.Form
{
    private System.ComponentModel.Container components = null;

    public Form1()
    {
        InitializeComponent();
    }

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

    private void InitializeComponent()
    {
        this.button1 = new System.Windows.Forms.Button();
        this.SuspendLayout();
        
        this.button1.Location = new System.Drawing.Point(440, 232);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(88, 32);
        this.button1.TabIndex = 0;
        this.button1.Text = "button1";
        this.button1.Click += new System.EventHandler(this.button1_Click);
        
        this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
        this.ClientSize = new System.Drawing.Size(528, 266);
        this.Controls.AddRange(new System.Windows.Forms.Control[] {this.button1});
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

    }    

    static void Main() 
    {
        Application.Run(new Form1());
    }

    private System.Windows.Forms.ListView listView1;
    private System.Windows.Forms.ColumnHeader columnFirstName;
    private System.Windows.Forms.ColumnHeader columnLastName;
    private System.Windows.Forms.ColumnHeader columnAge;
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.ColumnHeader columnWeight;

    private void InitListView()
    {
        // New up the variables
        listView1 = new ListView();
        columnFirstName = new ColumnHeader();
        columnLastName = new ColumnHeader();
        columnAge = new ColumnHeader();
        
        // Set position and size
        listView1.Bounds = new Rectangle(new Point(10,10), new Size(305,200));

        // Set the view to show details.
        listView1.View = View.Details;
        // Select the item and subitems when selection is made.
        listView1.FullRowSelect = true;
        // Set the default Sorting order
        listView1.Sorting = SortOrder.Ascending;
        // Display grid lines.
        listView1.GridLines = true;    

        // Set the text and width of the columns
        columnFirstName.Text = "First Name";
        columnFirstName.Width = 100;
        columnLastName.Text = "Last Name";
        columnLastName.Width = 100;
        columnAge.Text = "Age";
        columnAge.Width = 100;
        
        listView1.Columns.AddRange(new ColumnHeader[] {    columnFirstName, columnLastName, columnAge });
        this.Controls.Add(listView1);

        listView1.ColumnClick += new ColumnClickEventHandler(listView1_ColumnClick);
    }

    private void AddData(string fName, string lName, int Age)
    {
        ListViewItem lvi;

        string[] aHeaders = new string[3];
        
        aHeaders[0] = fName;
        aHeaders[1] = lName;
        aHeaders[2] = Age.ToString();

        lvi = new ListViewItem(aHeaders);
        listView1.Items.Add(lvi);        
    }

    public class Sorter : IComparer
    {
        // Initialize the variables to default
        public int column = 0;
        public bool bAscending = true;
                        
        // Using the Compare function of IComparer
        public int Compare(object x, object y)
        {
            // Cast the objects to ListViewItems
            ListViewItem lvi1 = (ListViewItem) x;
            ListViewItem lvi2 = (ListViewItem) y;

            // If the column is the string columns
            if(column != 2)
            {
                string lvi1String = lvi1.SubItems[column].ToString();
                string lvi2String = lvi2.SubItems[column].ToString();

                // Return the normal Compare
                if(bAscending)
                    return String.Compare(lvi1String, lvi2String);
                
                // Return the negated Compare
                return -String.Compare(lvi1String, lvi2String);
            }
            
            // The column is the Age column
            int lvi1Int = ParseListItemString(lvi1.SubItems[column].ToString());
            int lvi2Int = ParseListItemString(lvi2.SubItems[column].ToString());

            // Return the normal compare.. if x < y then return -1
            if(bAscending)
            {
                if(lvi1Int < lvi2Int)
                    return -1;
                else if(lvi1Int == lvi2Int)
                    return 0;

                return 1;
            }

            // Return the opposites for descending
            if(lvi1Int > lvi2Int)
                return -1;
            else if(lvi1Int == lvi2Int)
                return 0;

            return 1;
        }

        private int ParseListItemString(string x)
        {
            //ListViewItems are returned like this: "ListViewSubItem: {19}"
            int counter = 0;
            for(int i = x.Length - 1; i >= 0; i--, counter++)
            {
                if(x[i] == '{')
                    break;            
            }

            return Int32.Parse(x.Substring(x.Length - counter, counter-1));
        }
    }

    private void listView1_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
    {
        Sorter columnSorter = new Sorter();
        columnSorter.column = e.Column;
            
        if((columnSorter.bAscending = (listView1.Sorting == SortOrder.Ascending)))
            listView1.Sorting = SortOrder.Descending;
        else
            listView1.Sorting = SortOrder.Ascending;
        
        listView1.ListViewItemSorter = columnSorter;
    }

    private void button1_Click(object sender, System.EventArgs e)
    {
        InitListView();
        AddData("Victor", "Vuong", 19);
        AddData("Alan", "Gasperini", 20);
        AddData("John", "Gallardo", 22);
        AddData("Jessica", "Kim", 22);
        AddData("Nina", "Flores", 21);
        AddData("Baby", "Boy", 4);
        AddData("Old", "Fogie", 100);
    }
}


Return to Browsing Tutorials

Email this Tutorial to a Friend

Rate this Content:  
low quality  1 2 3 4 5  high quality

Reader's Comments Post a Comment
 
I forgot to include a picture of how the List View Tutorial looks like so here's a sample picture of it:


-- Victor Vuong, March 05, 2002
 
Great tutorial.
-- Brian Simoneau, March 05, 2002
 
Very good tutorial. Good commenting style (something most people seem to forget).
-- Michael S, March 06, 2002
 
Yes, great tutorial with explanations.
-- Reid Jonasson, March 06, 2002
 
I just finished spending a few more hours on a very nifty tool that vastly expands on this =) .. it's a secret! But I will post it when it's done.
-- Victor Vuong, March 06, 2002
 
very good tutorial...it will be helpful when i get my hands on a final copy of VS.NET to play around with.
-- J J, March 06, 2002
 
I just finished and submitted a new tool which I call the ObjectListView.

Basically it contains a ListView and automatically does sorting.. but guess what? You can pass it any type you want. You create columns and you pass it an IComparer per column or use the default one provided. You then can add any type of object.. for example

Dog x = new Dog("Fido");
myObjectListView.AddItem("string", 3812, x, 1.44);

It retains all the object information and then you can even retrieve from it. Hopefully my tool gets submitted and you guys can mess with my beta framework and improve on it =)
-- Victor Vuong, March 06, 2002
 
Good explanations, somethings needs more detail.
-- Gaurav Ahuja, March 07, 2002
 
I noticed that someone rated my tutorial a "1" which means that my work doesn't deserve to be shared with other members of Devhood. It's a rather harsh statement to make and I'm not one to deny criticism, but if you could please maybe post a comment on why my tutorial does a great disservice to the world.. or even privately e-mail telling me if you don't want to talk about it in public.

Hopefully the next time I write a tutorial I can take your points in mind and write something better next time. The point of a rating system is to tell the user what to improve on or let him know he did a good job. But rating a 1 without a comment, does absolutely nothing for me. So if you can.. thanks.
-- Victor Vuong, March 07, 2002
 
Don't get too discouraged. I thought you did an excellent job--and rated your project accordingly. The presentation was clear and easy to follow and above all, useful. Keep up the good work.
-- Ammon Beckstrom, March 08, 2002
 
Great tutorial: 5 Star Rating: *****
-- Michael S, March 08, 2002
 
Thanks a lot Ammon I was discouraged for a little while because someone was rating me a one on my tutorial and someone rated me a one on my tool. I guess it doesn't bother me anymore. It bothered me not in the fact that I was going to lose a point, but that it showed my work wasn't any good. But I'll take ratings less seriously and I'll still provide tutorials and tools I think would be beneficial to this community.

Can't let a few people stop the interest of the whole.
-- Victor Vuong, March 08, 2002
 
Excellent tutorial, very easy to follow.
I did rate it a three for two reasons:
1. Please don't give a tutorial where you hardcode something like the age column into the sorting. Show how the code could determine the whether to sort it numerically or by alpha.
2. Your tracking of the sorting means that if you sort ascending on column 1 and then click column two it will do a descend sort on column 2 which is not standard behavior.

Michael
-- Michael Bealer, April 19, 2002
 
Hey Michael.. you make valid points. I did revise my sorting stuff and make it sort with anything. I made this wrapper to sort it by any type of sort method you pass in, per column. It actually worked very very well. I was still touching up on it since I was new to inheriting controls or making a wrapper around a control. Thanks for your comments. Also the thing about clicking on another column and it switching the sorting order.. that was fixed too in my other project.
-- Victor Vuong, May 01, 2002
 
Copyright © 2001 DevHood® All Rights Reserved