Monday, September 11, 2006

DataGridView, DateTimePicker, and Nullable Types

I'm building a WinForms app with C# 2.0, and I need to display some data in a grid. I want to use the new DataGridView, since it supercedes the DataGrid.

One of the fields in the data being bound is a DateTime. I would like to display a DateTimePicker in that column when it's being edited.

That part isn't hard. One Yildirim Kocdag posted an article on CodeProject that includes a DataGridViewCalendarColumn. It's simple, yet effective. One hurdle vaulted.

But, the DateTime value can sometimes be null. I want the DateTimePicker to handle nulls appropriately. Once again to CodeProject: Claudio Grazioli wrote an article on implementing a nullable DateTimePicker. Two hurdles jumped, now. All I need to do is adapt the DataGridViewCalendarColumn to use a nullable DateTimePicker. This requires some changes to the nullable DateTimePicker and some additions to the DataGridViewCalendarColumn, but eventually it works.

The support for it in Visual Studio 2005 is beautiful, just beautiful: all I had to do was add a reference to the project in which I implemented the new controls and my DataGridViewCalendarColumn appears in a list with all the other DataGridViewColumns when you edit the columns of a DataGridView. I was so surprised to see it there already that I actually canceled out of that operation and double-checked that it really was my DataGridViewCalendarColumn that was appearing. It was. Three cheers for Visual Studio and three hurdles jumped.

So I try running the app to see how it works in context. For some reason it takes three clicks on a DataGridView cell to get an editable textbox, and it won't let me simply type in a date, but if you click on the Picker you can pick a date without difficulty. Clicking away from it updates the date in the data source. We're partway over the last hurdle: in mid-air, you might say.

But, I try to change an existing date to null and save that. I'm able to delete the contents of the DateTimePicker without problems, but when I click away the original date comes back. I do a little debugging and find that it's refusing to be set to the value I gave it for a null DateTime.

A bit of background: I hadn't decided just what flavor of nullable type I was going to use for my business objects, so in these initial forays I was simply using DateTime.MinValue as a proxy for Null (or DBNull). I forgot that the DateTimePicker doesn't like that: it only supports dates going back to 1753. That's why the cell won't accept the new, empty value: deep down within a Try... Catch, it's rejecting DateTime.MinValue with a supercilious message. It's serious about it, too: a quick check with Reflector reveals that 1/1/1753 is hardcoded in several places to make sure that no matter what anybody does with the MinValue property of the DateTimePicker, it doesn't go below 1753.

I momentarily consider expanding my definition of Null to include 1/1/1753 but immediately abandon that as A Very Bad Idea. So it looks like I have to make up my mind about nullable types right now.

So, to Nullable<T>. I love generics. But I already know that there's no easy conversion between DBNull and Nullable<T>. A little Googling reveals that WinForms databinding doesn't go out of its way to support Nullable<T>, either. In fact, it doesn't support it at all! But, again, there is a workaround! The very helpful Bruusi has posted a blog entry titled Data Binding and Nullable types in WinForms.NET.

The secret, it appears, is an extender provider. But his example is for binding to a TextBox. Can an extender provider be made to work with the DateTimePicker? In a DataGridView?

I don't know. But tune in tomorrow. I'll let you know.

7 Comments:

Blogger foson said...

So what happened in the end? I'm in suspense. I'm trying to do the same in VB.NET.

10/05/2006 12:33 PM  
Blogger Ann said...

In the end, I took a step back and reconsidered. I wasn't happy with the behavior of the DateTimePicker when the value was null. In order to change it from null to something else, you had to use the picking feature: you couldn't just tab in and start typing. So rather than mess with it some more I rolled my own DateTimePicker from the MaskedTextBox and the MonthCalendar, and built a DataGridViewColumn around that.

10/25/2006 6:24 PM  
Anonymous Anonymous said...

That sounds like a great idea! No one likes the null representation in DateTimePicker. Maybe you should publish your suggestion on www.codeproject.com ? I'd like to do the same, but I also need to use the time component and I don't know how I could gracefully integrate AM/PM selection (I don't want to use military time). Any ideas?

10/30/2006 5:21 PM  
Blogger H.Mehmet Yildirim Kocdag said...

Thanks for your nice comments. I am working on new version of GenericDataGridView, new features will be available. Bugs are going to be solved.

It will be publishing by the end of July 2007 on CodeProject.

5/31/2007 4:01 AM  
Blogger Unknown said...

I'm trying to do the same thing and came across the same articles for solutions, but haven't been successful integrating them. How did you get the datagridviewcolumn to it to work with a masked textbox and the monthcalendar? Thanks.

6/18/2007 2:37 PM  
Blogger Ann said...

katia: It was fairly simple. I created a composite control that had the masked text box and a button (to toggle the calendar). Various bits of code went into it to make sure only a valid date was entered. I then created a form without a border, header, or anything else, that was sized to the MonthCalendar and raised MonthCalendar events as its own events. When the button on my control was clicked, I opened the MonthCalendar form just below it. I used events and parameters to the constructor to hook things up so that calendar events were handled by the composite control.

I then used the example of the DateTimePicker DataGridViewColumn in Yildirim Kocdag's article (the same information can also be found in an example on MSDN) to wire my custom control into a DataGridViewColumn. It was just a question of figuring out what in my custom control corresponded to the properties of the DateTimePicker that the custom column was using.

I hope this helps somewhat. I don't have the code with me right now; I'll see if I can dig it out and be more specific.

7/12/2007 7:20 PM  
Blogger emilyGenesysGroup said...

Well, it sounds like you know what you're talking about, Anne. I wouldn't know. What I do know is that I am trying to find developers like you with C# 2.0 experience in the Dallas area. Do you know of anyone in that area? Are YOU in that area? I know. Just a shot in the dark, but I figured I'd try anyway! It's a great job in a highly collaborative environment in Coppell, TX. I can be reached at emily@genesysgroup.com

Thanks.

1/08/2008 2:16 PM  

Post a Comment

<< Home