views:

1416

answers:

4

I've got a DataGridView control in a Windows forms application. There are four columns with string data and three with DateTime data. I'm adding the rows programmatically using the Rows.Add() method. All of the columns have SortMode set to Automatic. Clicking the column headers to sort just works, except for the one DateTime column that has some nulls. When the user clicks that column's header, it throws an ArgumentException: Object must be of type DateTime.

I know the hard way to get around this: setting all of the SortModes to NotSortable, handling the ColumnHeaderMouseClick event and sorting the whole thing manually. I'm looking for the easy way.

Is there a property or something I can set, or some other relatively simple way to allow this column to sort with nulls in it?

+1  A: 

If you're adding rows dynamically I must assume the DataGridView is not Databound. In this case why don't you check for nulls when populating the correspondent row cells and instantiate some kind of dummy date (some funny date in the past so that it's clear that it was a null - maybe also specify that it's a default value) so that when it comes to sorting it's ll good.

If this ain't good - what about creating your own DataGridView exactly the same as the built-in one (using inheritance) BUT overriding the sorting method to ignore nulls?

JohnIdol
You're right, it's not DataBound. That's a good suggestion, but I'd prefer to keep the nulls in the display if I can. I'll keep that as plan B.
John M Gant
Mmm, ok - what about the new suggestion? see edit
JohnIdol
I may give that a shot. It's probably not any less work than manually handling the sorting, but it would at least be more interesting.
John M Gant
it is a probably cleaner approach than handling the sorting from some client object (the WinForm class itself would be a good candidate for that kind of mangling)
JohnIdol
A: 

Updated Answer: After looking at your posted answer, I see that you're checking for DBNull.Value in the cells. That may be the source of your problem since DBNull.Value can't be cast to a DateTime. If you're programmatically adding the rows, try replacing DBNull.Value with null (Nothing).


Original Answer: Try doing this for each column that holds DateTime data, preferably before any data is loaded:

myDateTimeColumn.ValueType = GetType(DateTime)

According to MSDN, the ValueType property is "used when filtering or sorting the columns with respect to the contents of their cells." So I would be sure to set it for any column that allows sorting.

With this property set, the grid can be sorted on a column that has nulls and will force inserted/updated cells to be converted to DateTime.

Kyle Gagnet
Thanks for the post. I'm defining the columns in the designer instead of programmatically, and I've got multiple columns. I wonder if either of those two differences could account for the different results.
John M Gant
A: 

Here's the solution I came up with. The DataGridView raises a SortCompare event that you can use to input custom sorting. I'm handling that event and making null values sort out higher than non-null values (you could just as easily make nulls lower than non-nulls). Here's the VB code. I'm assuming every cell value is IComparable (if not it will be handled by the normal error handling logic.)

Try
    If e.CellValue1 Is Nothing OrElse e.CellValue1.Equals(DBNull.Value) Then
        If e.CellValue2 Is Nothing OrElse e.CellValue2.Equals(DBNull.Value) Then
            e.SortResult = 0
        Else
            e.SortResult = 1
        End If
    Else
        If e.CellValue2 Is Nothing OrElse e.CellValue2.Equals(DBNull.Value) Then
            e.SortResult = -1
        Else
            e.SortResult = DirectCast(e.CellValue1, IComparable).CompareTo(DirectCast(e.CellValue2, IComparable))
        End If
    End If
    e.Handled = True
Catch ex As Exception
    HandleError("Error sorting result grid values", ex)
    Close()
End Try

If anybody has any improvements on this please feel free to post them.

John M Gant
See my updated answer. If what you have posted solves your problem, cool. But I'm interested to know if my suggestion also works for you.
Kyle Gagnet
A: 

Just to add some code to this question... I did the following to get sorting to work correctly for DateTime (and other non-string types) in the presence of nulls:

public MyFormCTor() {
    ...
    m_dataGridView.SortCompare += MySortCompare;
    ...
}

...

static void MySortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
    if (e.CellValue1 == e.CellValue2)
    {
        e.SortResult = 0;
        return;
    }

    if (e.CellValue1 == null)
    {
        e.SortResult = -1;
        return;
    }

    if (e.CellValue2 == null)
    {
        e.SortResult = 1;
        return;
    }

    e.SortResult = ((IComparable)e.CellValue1).CompareTo(e.CellValue2);
}
liwp