简体   繁体   中英

C#: Custom sort of DataGridView

I need to sort a DataGridView with Natural Sorting (Like in Explorer) so that numbers and text (in the same column) are sorted naturally, and not alphabetically (so that "place 3" comes before "place 20", etc.). I have a DataGridView, where I have set a DataView as DataSource. The DataView contains a DataTable which is created with some values from a database. The column types are string. I have an IComparer, which does what it should, but I can't figure out how to use it, cause I can't find out how to do the sorting. The DataGridView.SortCompare event, which would be perfect , doesn't work since it is databound. The DataView.Sort, only accept strings with column names and sort orders.

Very annoying. Tried to read related issues here on StackOverflow, and searched google lots and lots, but I can't really find much about this. Only stuff I really find is using that Sort(string) method of the dataview, which wont work, since it sorts alphabetically.

Does anyone know how to do this without too much trouble? It got to be others than me struggeling with this? I really don't want to re-implement the whole datagridview or dataview classes, just to get custom sorting...

Update : In case someone were wondering, I'm still looking for a good answer to this problem. Although in the mean time, I ended up creating my own simple table class, and then feed that into a datagridview manually. Overriding the SortCompare method. Bit annoying, but wasn't too hard, since I only need to show values (no editing or anything) and therefore could convert everything to strings.

Take a look at this MSDN page and this blog post . In principle, you need to configure the sorting at the data source (whether its an ObjectDataSource or a SqlDataSource) not at the GridView.

As far as I can tell the DataView class doesn't support anything other than a simple ascending/decending sort. Without seeing the code where you load and bind the data it's hard to make a specific recommendation, but you could either:

  1. Load your data into a List instead of a DataTable, call the Sort method passing in your comparison method and then bind to that list.
  2. Create an ObjectDataSource in your aspx code that gets the data directly from a class, and configure that ObjectDataSource to use your IComparer.

This code should work. It is similar to ListView's ListViewItemSorter. Using IComparer.

To use:

private void dgv_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
    MyDataGridHelper.DataGridSort(dgv, e.ColumnIndex);
}

MyDataGridHelper.cs:

public class MyDataGridHelper
{
    public static void DataGridSort(DataGridView dgv, int column)
    {
        DataGridViewCustomSorter dgvSorter = null;
        if (dgv.Tag == null || !(dgv.Tag is IComparer))
        {
            dgvSorter = new DataGridViewCustomSorter(dgv);
            dgv.Tag = dgvSorter;
        }
        else
        {
            dgvSorter = (DataGridViewCustomSorter)dgv.Tag;
        }
        dgvSorter.SortColumn = column;
        dgv.Sort(dgvSorter);
    }

    private class DataGridViewCustomSorter : IComparer
    {
        private int ColumnIndex;
        private SortOrder OrderOfSort;
        private DataGridView myDataGridView;
        private TypeCode mySortTypeCode;

        public DataGridViewCustomSorter(DataGridView dgv)
        {
            myDataGridView = dgv;
            mySortTypeCode = Type.GetTypeCode(Type.GetType("System.String"));
            ColumnIndex = 0;
            OrderOfSort = SortOrder.None;
        }

        public int Compare(object x, object y)
        {
            int result;
            DataGridViewRow dgvX, dgvY;

            dgvX = (DataGridViewRow)x;
            dgvY = (DataGridViewRow)y;
            string sx = dgvX.Cells[ColumnIndex].Value.ToString();
            string sy = dgvY.Cells[ColumnIndex].Value.ToString();

            //null handling
            if (sx == String.Empty && sy == String.Empty)
                result = 0;
            else if (sx == String.Empty && sy != String.Empty)
                result = -1;
            else if (sx != String.Empty && sy == String.Empty)
                result = 1;
            else
            {
                switch (mySortTypeCode)
                {
                    case TypeCode.Decimal:
                        Decimal nx = Convert.ToDecimal(sx);
                        Decimal ny = Convert.ToDecimal(sy);
                        result = nx.CompareTo(ny);
                        break;
                    case TypeCode.DateTime:
                        DateTime dx = Convert.ToDateTime(sx);
                        DateTime dy = Convert.ToDateTime(sy);
                        result = dx.CompareTo(dy);
                        break;
                    case TypeCode.String:
                        result = (new CaseInsensitiveComparer()).Compare(sx, sy);
                        break;
                    default:
                        result = (new CaseInsensitiveComparer()).Compare(sx, sy);
                        break;
                }
            }
            if (OrderOfSort == SortOrder.Descending)
                result = (-result);

            return result;
        }

        public int SortColumn
        {
            set
            {
                if (ColumnIndex == value)
                {
                    OrderOfSort = (OrderOfSort == SortOrder.Descending ? SortOrder.Ascending : SortOrder.Descending);
                }
                ColumnIndex = value;
                try
                {
                    mySortTypeCode = Type.GetTypeCode(Type.GetType((myDataGridView.Columns[ColumnIndex]).Tag.ToString()));
                }
                catch
                {
                    mySortTypeCode = TypeCode.String;
                }
            }
            get { return ColumnIndex; }
        }

        public SortOrder Order
        {
            set { OrderOfSort = value; }
            get { return OrderOfSort; }
        }
    } //end class DataGridViewCustomSorter
} //end class MyDataGridHelper

Here there is some solution " Custom Sorting Using the SortCompare Event " and " Custom Sorting Using the IComparer Interface ":

http://msdn.microsoft.com/en-us/library/ms171608.aspx

You can create 2 hidden columns. Assign the text part to the 1st hidden column and the number part to the 2nd hidden column. Now sort by these hidden columns (alpha sort for 1st column & numeric sort for the 2nd column).

In this way, you can retain the original column for display purposes & have the 2 hidden columns for sorting.

You could move the sort logic into your database query and have it return an additional column which had the correct sort order.

Then (along the lines of @True C Sharp's answer) you could have a hidden column containing this value and sort by this rather than by the display column.

This assumes that the logic for determining the sort order can be performed in SQL. This may not work if the algorithm for determining the sort order is complex.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM