How do I sort the items of a ListView when the ListView is in VirtualMode?

I understand that I need to sort the collection in which the ListView collects 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, which in turn adds items very slowly, so I have to use VirtualMode in my GUI-ListView in the first place.

Does anyone know how to do this or point me in the right direction?

+2


source to share


5 answers


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

I did a quick google search for listview sort virtualmode . The first result was this page , where the quote is taken from.

For example, if your data source is a DataView, apply a sort instead of this ListView.



If it's only about performance when adding elements, I would do it like barism ; use BeginUpdate / EndUpdate instead of VirtualMode.

try {
  listView1.BeginUpdate();
  // add items
}
finally {
  listView1.EndUpdate();
}

      

+3


source


If you are using virtual mode, you need to sort the original data source. As you might have discovered, ListViewItemSorter does nothing for virtual lists.

If you are using a non-virtual list, you can also use AddRange (), which is significantly faster than the Add () series - Apart from using BeginUpdate / EndUpdate already described.



The ObjectListView (an open source wrapper around the .NET WinForms ListView) already uses all of these techniques to make itself fast. This is a big improvement over a regular ListView. It supports both normal mode and live mode list and makes them much easier to use. For example, sorting is done completely automatically.

+2


source


Have you tried beginupdate () and endupdate ()? Adding data is much faster if you use beginupdate / endupdate. (When you call beginupdate, the listview does not paint until you call endupdate)

listView1.BeginUpdate();
for (int i = 0; i < 20000; i++)
{
listView1.Items.Add("abdc", 1);
}
listView1.EndUpdate();

      

+1


source


For very large lists, VirtualView ListView is the answer for a specific one. In non virtual mode, it seems to have drawn the entire list and then copied it into the view, while in virtual mode, it just draws the ones visible in the view. In my case, the list was 40K + records. In non-virtual mode, the ListView may take several minutes to update. In virtual mode, it was instant.

To sort the list, you must sort the underlying data source as mentioned. This is the easy part. You will also need to force refresh the display, which will not be done automatically. You can use ListView.TopItem.Index to find the index in the underlying data source that matches the top row of the virtual List before sorting. There is also an API call that returns the number of display rows in the ListView, which can be implemented as a C # function, for example:

public const Int32 LVM_GETCOUNTPERPAGE = 0x1040;

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

      

This will allow you to calculate the range that you should update. The only remaining question is how do you reconcile the existing display with what will be displayed after the data is sorted. If you want to keep the same data item at the top row, you must have some mechanism to find its new index in the newly sorted list, so you can replace it at the top position - something essentially equivalent to SQL IDENTITY ...

+1


source


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

First step I am populating a ListViewItem, not a ListView.Items collection:

private List<ListViewItem> _ListViewItems;

      

Then I applied RetrieveVirtualItem method

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

      

Finally, I am sorting my ListViewItem using the same class as before, I only needed to change the base class

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

      

This is my implementation of the IComparer class:

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 helps you.

+1


source







All Articles