2

I realize i have to sort the collection where the ListView gathers the items from:
ListView listCollection = new ListView();

But this doesn't seem to work unless the ListView is added as a GUI-control to the form, that in turn makes it very slow to add items to, hence why i have to use VirtualMode on my GUI-ListView in the first place.

Anyone know how to go about this or point me in the right direction?

5 Answers5

3

basically, you will need to apply sort to the data pump itself.

I did a quick search on Google for listview sort virtualmode. First result was this page, where the above quote was taken from.

For example, if your datasource is a DataView, apply sorting on this instead of the ListView.

If it is just a question of performance when adding items, I would do as barism suggests; use BeginUpdate/EndUpdate instead of VirtualMode.

try {
  listView1.BeginUpdate();
  // add items
}
finally {
  listView1.EndUpdate();
}
Community
  • 1
  • 1
bernhof
  • 6,219
  • 2
  • 45
  • 71
  • Program still hangs, i must use VM because the list can become very large (50k+ items) –  Sep 02 '09 at 12:03
  • 1
    Well, in that case (if you absolutely need to load 50k+ items) I would store the items in a list of some kind, and sort the items there (as mentioned, you need to apply the sort to the data pump itself), though you'll obviously encounter some delay when sorting this many items, unless you find some super-sorting algorithm that I'm not familiar with :-) (which is not completely unthinkable) – bernhof Sep 02 '09 at 15:26
3

If you are using virtual mode, you have to sort your underlying data source. As you may have found, ListViewItemSorter does nothing for virtual lists.

If you are using a non-virtual listview, you can also use AddRange(), which is significantly faster than a series of Add() -- In addition to using BeginUpdate/EndUpdate that has already been described.

ObjectListView (an open source wrapper around .NET WinForms ListView) already uses all these techniques to make itself fast. It is a big improvement over a normal ListView. It supports both normal mode and virtual mode listviews, and make them both much easier to use. For example, sorting is handled completely automatically.

Grammarian
  • 6,774
  • 1
  • 18
  • 32
2

I had the same problem switching VirtualMode True with an existing project, but the solution was surprisingly easy:

First step I am populating a list of ListViewItem, instead of ListView.Items collection:

private List<ListViewItem> _ListViewItems;

Then I have implemented the RetrieveVirtualItem method

private void mLV_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
    e.Item = _ListViewItems[e.ItemIndex];
}

Finally I am sorting my list of ListViewItem using the same class I was using before, I had just only to change the base class

_ListViewItems.Sort((System.Collections.Generic.IComparer<ListViewItem>)new ListViewItemComparer(new int[] { e.Column }, mLV.Sorting));

This is my IComparer class implementation:

class ListViewItemComparer : System.Collections.Generic.IComparer<ListViewItem>
{
    int[] mColonne;
    private System.Windows.Forms.SortOrder order;
    public ListViewItemComparer(int[] mCols)
    {
        this.mColonne = mCols;
        this.order = System.Windows.Forms.SortOrder.Ascending;
    }

    public ListViewItemComparer(int[] mCols, System.Windows.Forms.SortOrder order)
    {
        this.mColonne = mCols;
        this.order = order;
    }

    public int Compare(ListViewItem x, ListViewItem y)
    {
        int returnVal = -1;

        foreach (int mColonna in mColonne)
        {
            double mNum1;
            double mNum2;

            String mStr1 = "";
            String mStr2 = "";

            if ((x.SubItems[mColonna].Text == "NULL") && (x.SubItems[mColonna].ForeColor == Color.Red))
            {
                mStr1 = "-1";
            }
            else
            {
                mStr1 = x.SubItems[mColonna].Text;
            }

            if ((y.SubItems[mColonna].Text == "NULL") && (y.SubItems[mColonna].ForeColor == Color.Red))
            {
                mStr2 = "-1";
            }
            else
            {
                mStr2 = y.SubItems[mColonna].Text;
            }


            if ((double.TryParse(mStr1, out mNum1) == true) && (double.TryParse(mStr2, out mNum2) == true))
            {
                if (mNum1 == mNum2)
                {
                    returnVal = 0;
                }
                else if (mNum1 > mNum2)
                {
                    returnVal = 1;
                }
                else
                {
                    returnVal = -1;
                }
            }
            else if ((double.TryParse(mStr1, out mNum1) == true) && (double.TryParse(mStr2, out mNum2) == false))
            {
                returnVal = -1;
            }
            else if ((double.TryParse(mStr1, out mNum1) == false) && (double.TryParse(mStr2, out mNum2) == true))
            {
                returnVal = 1;
            }
            else
            {
                returnVal = String.Compare(mStr1, mStr2);
            }

            if (returnVal != 0)
            {
                break;
            }
        }

        // Determine whether the sort order is descending.
        if (order == System.Windows.Forms.SortOrder.Descending)
        {
            // Invert the value returned by String.Compare.
            returnVal *= -1;
        }
        return returnVal;
    }
}

Hope this will help you.

Riccardo
  • 21
  • 1
1

For very large lists, the Virtual Mode ListView is the answer for certain. In non-virtual mode it seems to draw the entire list and then clip it to the view, while in Virtual mode it simply draws the ones in the view. In my case the list was 40K+ records. In non-virtual mode an update to the ListView could take minutes. In virtual mode it was instantaneous.

To sort the list, you must sort the underlying datasource, as has already been mentioned. That is the easy part. You will also need to force a refresh to the display, which is not done automatically. You can use ListView.TopItem.Index to find the index in the underlying data source that corresponds to the top row of the Virtual ListView before the sort. There is also an API call that returns the number of display rows in the ListView that you can implement as C# function, like this:

public const Int32 LVM_GETCOUNTPERPAGE = 0x1040;

public static int GetListViewRows( ListView xoView ) 
{
   return (int)WindowsMessage.SendMessage( xoView.Handle, LVM_GETCOUNTPERPAGE, 0, 0 );
}

That will let you calculate the range you have to update. About the only remaining question is how you reconcile the existing display with what will appear after the data have been sorted. If you want to leave the same data element in the top row, you must have some mechanism in place that will find its new index in the newly sorted list, so you can replace it in the top position - something essentially equivalent to an SQL IDENTITY.

David
  • 76
  • 1
1

Did you try beginupdate() and endupdate()? Adding data is much faster when you use beginupdate/endupdate.(when you call beginupdate, listview doesn't draw until you call endupdate)

listView1.BeginUpdate();
for (int i = 0; i < 20000; i++)
{
listView1.Items.Add("abdc", 1);
}
listView1.EndUpdate();
bmeric
  • 1,124
  • 1
  • 17
  • 27
  • Does not help app still hangs when adding maaany items. –  Sep 02 '09 at 12:44
  • You can create a thread for adding items.(or call processmessages() after every Items.Add() but threads are far better than processmessages) – bmeric Sep 02 '09 at 13:43