Search code examples
c#winformssortingdatagridview

Sorting unbound DataGridView column, alphanumerically in Windows Forms app


I can't seem to find a current updated answer. The basic functionality works for me, where it will automatically sort a list like:

001.15
1
2
3
some
string

but my biggest issue is if I added a number, say '100', my list will now be:

001.15
1
100
2
3
some
string

I would like to see 100 after 3.

The single column I'm looking to sort by, 'Test ID', will contain strings, integers, and doubles.

I've tried many things, including a method called every time a button is pressed:

private void SortDVG(object sender, DataGridViewSortCompareEventArgs e)
{
     if (e.Column.Index == 0)
     {
         e.SortResult = int.Parse(e.CellValue1.ToString()).CompareTo(int.Parse(e.CellValue2.ToString()));
         e.Handled = true;
     }
 }

with a reference in a button click as

SortDVG(dataGridView1, (DataGridViewSortCompareEventArgs)e);

but this throws a runtime errors because it registers as a mouse event, which makes sense to me.

System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Forms.MouseEventArgs' to type 'System.Windows.Forms.DataGridViewSortCompareEventArgs'.'

and I've even tried just one line (this is what gave me the ordering in the first place)

dataGridView1.Sort(dataGridView1.Columns[0], ListSortDirection.Ascending);

At a bit of a loss right now and looking to be pointed in the right direction


Solution

  • If your goal is to perform a strict alphanumeric sort on a column that contains mixed types, you can safely achieve this by handling the SortCompare event and:

    1. Get string values for e.CellValue1 and e.CellValue2, converting any null values to string.Empty.
    2. Set the SortResult by comparing the strings.
    3. IMPORTANT: Set e.Handled to true

    ascending and descending sorts


    public partial class MainForm : Form
    {
        public MainForm() => InitializeComponent();
    
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            dataGridView.Columns.Add(new DataGridViewTextBoxColumn
            {
                Name = "TestId",
                AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells,
            });
            dataGridView.Columns.Add(new DataGridViewTextBoxColumn
            {
                Name = "Description",
                AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
            });
            dataGridView.SortCompare += DataGridView_SortCompare;
            dataGridView.Rows.Add(001.15, "First item with decimal");
            dataGridView.Rows.Add(1, "Single digit integer");
            dataGridView.Rows.Add(100, "Triple digit integer");
            dataGridView.Rows.Add(2, "Another single digit integer");
            dataGridView.Rows.Add(3, "Yet another single digit integer");
            dataGridView.Rows.Add("some string", "String example");
        }
    
        private void DataGridView_SortCompare(object? sender, DataGridViewSortCompareEventArgs e)
        {
            var a = e.CellValue1?.ToString() ?? string.Empty;
            var b = e.CellValue2?.ToString() ?? string.Empty;
            e.SortResult = a.CompareTo(b);
            e.Handled = true;
        }
    }
    

    Alternative: Sort numbers with numbers

    If on the other hand you want a kind of hybrid sort, where "numbers are compared to numbers" then you will get a slightly different result by attempting conversions to double.

    hybrid sort

    private void DataGridView_SortCompare(object? sender, DataGridViewSortCompareEventArgs e)
    {
        var a = e.CellValue1?.ToString() ?? string.Empty;
        var b = e.CellValue2?.ToString() ?? string.Empty;
        if (double.TryParse(a, out double value1) &&
            double.TryParse(b, out double value2))
        {
            e.SortResult = value1.CompareTo(value2);
        }
        else
        {
            e.SortResult = a.CompareTo(b);
        }
        e.Handled = true;
    }
    

    NOTE: By default, you can sort on a column by clicking the column header. You can also call DataGridView.Sort(...) to do the same thing programmatically.