0

I have a datagrid with a column that allows entry by the user of strings which may contain numbers. The column I have made sorted programmatically and one other automatically. I have looked at all available information on this and other websites but have found nothing that works or in some cases that I am able to know how to implement. Many of the solutions use Icomparer but few are specifically for datagridview. This is an example of the column data after performing datagridview1.Sort(Column1, ListSortDirection.Ascending);

1
140
2b
40
70
7a
9
aa
aa30
aa5
bc
de

I have introduced the MyDatagridHelper Class and relevant code using the solution from this site: C#: Custom sort of DataGridView, but alas it gives the following results

aa
bc
de
aa30
2b
7a
70
1
40
140
9
aa5

All other solutions are not specific to datagridview. I am self taught and relatively new to coding, so I am not able to make sense of the code within. I have tried as best I can to find commonality and cut and paste but to no avail. Some examples I thought showed promise and might do the job were: https://www.codeproject.com/articles/22517/natural-sort-comparer and Natural Sort Order in C#

I effectively need help on how to implement these or any other solutions that might do the job. I have not included the second column for simplicity as it is not relevant to the question. Thanks

Community
  • 1
  • 1
Big Vil
  • 7
  • 6

1 Answers1

0

The code project article 22517 that you link to has the logic in there that you require, it just needs some tweaking for use with a DataGridView by creating an implementation of System.Collections.IComparer rather than System.Collections.Generic.Comparer

So if you create yourself a new class in your project something like this:

public class NaturalSortComparer : System.Collections.IComparer {

    private System.Collections.Generic.Dictionary<string, string[]> table;

    public NaturalSortComparer() {
        table = new System.Collections.Generic.Dictionary<string, string[]>();
    }

    public void Dispose() {
        table.Clear();
        table = null;
    }

    public int Compare(object x, object y) {
        System.Windows.Forms.DataGridViewRow DataGridViewRow1 = (System.Windows.Forms.DataGridViewRow)x;
        System.Windows.Forms.DataGridViewRow DataGridViewRow2 = (System.Windows.Forms.DataGridViewRow)y;

        string xStr = DataGridViewRow1.Cells["Column1"].Value.ToString();
        string yStr = DataGridViewRow2.Cells["Column1"].Value.ToString();


        if (xStr == yStr) {
            return 0;
        }
        string[] x1, y1;
        if (!table.TryGetValue(xStr, out x1)) {
            x1 = System.Text.RegularExpressions.Regex.Split(xStr.Replace(" ", ""), "([0-9]+)");
            table.Add(xStr, x1);
        }
        if (!table.TryGetValue(yStr, out y1)) {
            y1 = System.Text.RegularExpressions.Regex.Split(yStr.Replace(" ", ""), "([0-9]+)");
            table.Add(yStr, y1);
        }

        for (int i = 0; i < x1.Length && i < y1.Length; i++) {
            if (x1[i] != y1[i]) {
                return PartCompare(x1[i], y1[i]);
            }
        }
        if (y1.Length > x1.Length) {
            return 1;
        } else if (x1.Length > y1.Length) {
            return -1;
        } else {
            return 0;
        }
    }

    private static int PartCompare(string left, string right) {
        int x, y;
        if (!int.TryParse(left, out x)) {
            return left.CompareTo(right);
        }

        if (!int.TryParse(right, out y)) {
            return left.CompareTo(right);
        }

        return x.CompareTo(y);
    }
}

You can see in here that I have hard coded it to used a column named "Column1" as per your example, but you can change this to be more dynamic.

When you then sort your grid, you can pass in a new instance of this class you have just created, like this:

dataGridView1.Sort(new NaturalSortComparer());
Gary Wright
  • 2,441
  • 1
  • 20
  • 32
  • Thanks so much as this has caused me considerable grief. Knew what I needed to do and tried the sort with the single parameter but integrating the two was the problem. This is a great help! I do have however an empty string ie "" as the first entry . With this method, the empty cell ends up in the middle between the entries with digits and those with letters. Is there a way to tweak this further to make the empty cell the first entry in the first cell, then numbers, then letters? I want this to be the default entry for the datagrid. – Big Vil Dec 21 '16 at 20:14
  • I inserted the following after the line that commences "string yStr = ..." if (xStr == "") { return 0; } – Big Vil Dec 22 '16 at 11:39
  • It then places the empty cell at the top, or first row of the datagrid – Big Vil Dec 22 '16 at 11:42
  • Have also noticed the code as stands treats lower vs upper case differently. It will place test20 before Test2. If you convert xStr and yStr .ToLower() it works as expected. This is also true of the codeproject article cited earlier, a variation of which I had to use to sort a list for a combobox. – Big Vil Jan 03 '17 at 11:05
  • Sorry for the lack of reply to the above, but it looks like you have overcome the issues you have found. Glad I could help with the initial quandary :) – Gary Wright Jan 03 '17 at 14:24
  • Still wondered if there was a way to share the code for sorting both datagrids and lists? I have had to create two separate classes with essentially the same code. One deals with strings, the other objects. I have tried but to no avail. Thanks – Big Vil Jan 04 '17 at 20:28
  • Yes, there are lots of ways to do this - I suggest you post this as a new question, including the code for your two classes. If I see it, I'll answer it for you – Gary Wright Jan 06 '17 at 12:06