Bohn
Bohn

Reputation: 26929

Alphabetically Sorting the items on a ListView

I have a listview like this and in FormLoad event I should do some initializations for it such as this: ( I need these ) .

    listView.Scrollable = true;
    listView.HideSelection = false;
    listView.FullRowSelect = true;
    listView.View = View.Details;
    listView.HeaderStyle = ColumnHeaderStyle.None;
    ColumnHeader header = new ColumnHeader();
    header.Text = "MyHdr";
    header.Name = "MyCol";
    header.Width = listView.ClientSize.Width;
    listView.Columns.Add(header);

and the way I am adding items to it is pretty simple like this:

listView.Items.Add("hello");
listView.Items.Add("How are you");
//... etc

But I want them to be added and sorted alphabetically but when I add a new item to it and call Sort method, it doesn't do anything. Why?! :(

EDIT: This is the whole section that at its last line I am calling Sort() The goal is to have two list views, and a Move Button, when Move button is clicked the selected items from one listview should get moved to the other listview. (Both listviews don't need to be sorted. Just the AvailLV listview should be sorted )

private void MoveBtn_Click(object sender, EventArgs e)
{
    ListView source=null;
    ListView target= null;

    if(AvailableLV.SelectedItems.Count>0)
    {
        source = AvailableLV;
        target = SelectedLV;
    }

    if(SelectedLV.SelectedItems.Count>0)
    {
        source = SelectedLV;
        target = AvailableLV;
    }

    if (source != null && target != null)
    {
        HaulItems(source, target);
    }
}

private void HaulItems(ListView source , ListView target)
{
    foreach(ListViewItem item in source.Items)
    {
        if(item.Selected)
        {
            source.Items.Remove(item);
            target.Items.Add(item);
        }
    }
    AvailableLV.Sort();
}

Upvotes: 2

Views: 27877

Answers (4)

mike_coreit
mike_coreit

Reputation: 1

If you are only looking for a one-time sort and you have access to the data before it is added to the ListView then this is a tidy solution:

        string[] exampleData = { "1", "2", "3" };
        string[] namesToSortBy = { "Sam", "Bill", "Jeff" };

        SortedList<string, ListViewItem> alphabetical = new SortedList<string, ListViewItem>();

        for (int i = 0; i < exampleData.Length; i++)
        {
            ListViewItem lvi = new ListViewItem();

            lvi.Text = exampleData[i];

            lvi.SubItems.Add(namesToSortBy[i]);

            alphabetical.Add(namesToSortBy[i], lvi);
        }

        foreach (KeyValuePair<string, ListViewItem> item in alphabetical)
        {
            listView1.Items.Add(item.Value);
        }

Upvotes: 0

TaW
TaW

Reputation: 54453

Yet another later-comer to the ball-game.

This solution uses Linq to sort the items.

It can sort by clicking the column headers. The sort is by numbers, strings or dates, it will toggle between ascending and descending and it ignores invalid data, sorting them to the beginning or end.

You need to feed in the types of the columns in a simple string.

static class LvHelper
{
    public static void SortByColumn(this ListView lv, string colTypes, 
                                    ColumnClickEventArgs e)
    {
        string lvSort = "As0";
        if (lv.Tag != null) lvSort = lv.Tag.ToString();
        if (e.Column < 0 || e.Column > colTypes.Length - 1) return;
        char sortType = colTypes[e.Column];
        if (sortType == '-') return;
        int mini = lv.Items.Cast<ListViewItem>().Select(x => x.SubItems.Count).Min();
        if (sortType != 's'  && lv.Items.Cast<ListViewItem>()
                            .Select(x => x.SubItems.Count - 1).Min() < e.Column) return;


        int  sortCol = Convert.ToInt32(lvSort.Substring(2));
        bool asc = lvSort[0] == 'A';
        if (e.Column == sortCol) asc = !asc;
        DateTime dummyD;
        double dummyN;
        double maxDate = DateTime.MaxValue.ToOADate();
        int order = asc ? 1 : -1;

        List<ListViewItem> sorted = null;
        try
        {
            if (sortType == 'n')   // numbers
                sorted = lv.Items.Cast<ListViewItem>().Select(x => x)
                         .OrderBy(x => order * Convert.ToDouble(
                          double.TryParse(x.SubItems[e.Column].Text, out dummyN)
                                ? x.SubItems[e.Column].Text 
                                : (double.MinValue / 2).ToString())).ToList();
            else if (sortType == 'd')   // dates
                sorted = lv.Items.Cast<ListViewItem>().Select(x => x)
                         .OrderBy(x => (Convert.ToDateTime(
                          DateTime.TryParse(x.SubItems[e.Column].Text, out dummyD)
                                  ? x.SubItems[e.Column].Text 
                                  : "1900-01-01").ToOADate() * order)).ToList();

            else  // strings
            {
                if (asc)
                    sorted = lv.Items.Cast<ListViewItem>().Select(x => x)
                               .OrderBy(x => x.SubItems.Count -1 < e.Column 
                               ? "" : (x.SubItems[e.Column].Text)).ToList();
                else sorted = lv.Items.Cast<ListViewItem>().Select(x => x)
                               .OrderByDescending(x => x.SubItems.Count -1 < e.Column 
                               ? "" : (x.SubItems[e.Column].Text)).ToList();
            }
        }
        catch (ArgumentOutOfRangeException ex) { return; }

        lv.Items.Clear();
        lv.Items.AddRange(sorted.ToArray());

        lv.Tag = "" + (asc ? "A" : "D") + sortType.ToString() + e.Column;
    }

}

The solution uses the LV's Tag to remember the current sort.

The solution expects data to be there, so missing subitems will make the sort fail.

It is written as an extension method, so after adding the class you can call it on any ListView :

private void someListView_ColumnClick(object sender, ColumnClickEventArgs e)
{
  ListView lv = sender as ListView;
  string colTypes = "sds-n";   // string, date, string, excluded, number
  lv.SortByColumn(colTypes, e);
}

Upvotes: 1

Dr Archer
Dr Archer

Reputation: 348

If anyone pays attention to this topic, I found the easiest route is to send the listview to a datatable and create a dataview from it and then sort the dataview. After that dataview is sorted, create a temporary table and then push the rows back into the listview. Ex below.

    public string SortOrder;
    public string ItemSorted;
    private void LSTHistory_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        DataTable TempTable = new DataTable();
        for (int i = 0; i < LSTHistory.Columns.Count; i++)
        {
            TempTable.Columns.Add(LSTHistory.Columns[i].Text);
        }
        foreach (ListViewItem Item in LSTHistory.Items)
        {
            DataRow iRow = TempTable.NewRow();
            iRow[0] = Item.Text;
            iRow[1] = Item.SubItems[1].Text;
            TempTable.Rows.Add(iRow);
        }
        if (SortOrder == string.Empty || SortOrder == "ASC") SortOrder = "DESC";
        else SortOrder = "ASC";
        if (e.Column == COLTime.Index)
        {
            ItemSorted = COLTime.Text;
        }
        else
        {
            ItemSorted = COLURL.Text;
        }
        DataView OldView = TempTable.DefaultView;
        OldView.Sort = ItemSorted + " " + SortOrder;
        DataTable SortedTable = OldView.ToTable();
        LSTHistory.Items.Clear();
        foreach (DataRow iRow in SortedTable.Rows)
        {
            LSTHistory.Items.Add(iRow[0].ToString()).SubItems.Add(iRow[1].ToString());
        }
    }

Upvotes: 1

Mark Hall
Mark Hall

Reputation: 54562

Where are you setting your ListView.Sorting Property

From above link:

The Sorting property allows you to specify whether or not items are sorted in the ListView control. By default, no sorting is performed. When the Sorting property is set to Ascending or Descending, the items in the ListView are sorted automatically in ascending alphabetical order (when the property is set to Ascending) or descending alphabetical order (when the property is set to Descending). You can use this property to automatically sort items that are displayed in your ListView control to make it easier for users to find items when a large number of items are available.


Looking at your edit, I think all you need to do is set the ListView.Sorting Property on AvailableLV and it will automatically sort your items as they are added. or instead of calling.

AvailableLV.Sort(); 

use

AvailableLV.Sorting = SortOrder.Ascending;

Upvotes: 6

Related Questions