views:

3313

answers:

3

I have a generic

List<MyClass>

where MyClass has a property "InvoiceNumber" which contains values like:

200906/1
200906/2
..
200906/10
200906/11
200906/12

My list is bound to a

BindingList<T>

which supports sorting with linq:

protected override void ApplySortCore(
           PropertyDescriptor property, ListSortDirection direction)
{

    _sortProperty = property;
    _sortDirection = direction;

    var items = this.Items;

    switch (direction)
    {
        case ListSortDirection.Ascending:
            items = items.OrderByDescending(x => property.GetValue(x)).ToList();
            break;
        case ListSortDirection.Descending:
            items = items.OrderByDescending(x => property.GetValue(x)).ToList();
            break;
    }

    this.Items = items;

}

However the default comparer sorts (as supposed) like this:

200906/1
200906/10
200906/11
200906/12
200906/2

which is nasty in this case.

Now I want to use my own IComparer with this. It looks like this:

    public class MyComparer : IComparer<Object>
    {

        public int Compare(Object stringA, Object stringB)
        {
            String[] valueA = stringA.ToString().Split('/');
            String[] valueB = stringB.ToString().Split('/');

            if(valueA .Length != 2 || valueB .Length != 2)
                 return String.Compare(stringA.ToString(), stringB.ToString());

            if (valueA[0] == valueB[0]) 
            {
              return String.Compare(valueA[1], valueB[1]);
            }
            else
            {
              return String.Compare(valueA[0], valueB[0]);
            }

        }

    }

and changed the ApplySortCore code to use this IComparer:

        case ListSortDirection.Ascending:
            MyComparer comparer = new MyComparer();
            items = items.OrderByDescending(
                      x => property.GetValue(x), comparer).ToList();
            break;

I can debug my code and the that MyComparer.Compare(object, object) is called multiple times and returns the right values (-1, 0, 1) for a compare method.

But my list is still sorted the "wrong" way. Am I missing something? I have no clu.

+2  A: 

Your comparer looks wrong to me. You're still just sorting in the default text ordering. Surely you want to be parsing the two numbers and sorting based on that:

public int Compare(Object stringA, Object stringB)
{
    string[] valueA = stringA.ToString().Split('/');
    string[] valueB = stringB.ToString().Split('/');

    if (valueA.Length != 2 || valueB.Length != 2)
    {
        stringA.ToString().CompareTo(stringB.ToString()));
    }

    // Note: do error checking and consider i18n issues too :)
    if (valueA[0] == valueB[0]) 
    {
        return int.Parse(valueA[1]).CompareTo(int.Parse(valueB[1]));
    }
    else
    {
        return int.Parse(valueA[0]).CompareTo(int.Parse(valueB[0]));
    }
}

(Note that this doesn't sit well with your question stating that you've debugged through and verified that Compare is returning the right value - but I'm afraid I suspect human error on that front.)

Additionally, Sven's right - changing the value of items doesn't change your bound list at all. You should add:

this.Items = items;

at the bottom of your method.

Jon Skeet
Sorry, I shortend the code a little. In my original code I do this.Items = items; (If not it wouldn't sort anyway) But int conversion works (I must have been blind || stupid to miss that). Thx very much.
SchlaWiener
+1  A: 

The sorted list is only bound to the local variable items, not to the Items property of your binding list, hence it remains unsorted.

[Edit] Basically, you're simply throwing away the result of your sorting efforts ;-)

Sven Künzler
+1  A: 

I encountered the issue of general natural sorting and blogged the solution here:

Natural Sort Compare with Linq OrderBy()

James McCormack