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.

Tuesday, September 05, 2006

"Just like CodeSmith, only more complicated"

That's my initial response to Microsoft's Guidance Automation Toolkit. It combines Visual Studio add-ins with code templates that look remarkably like CodeSmith's. Programming of "recipes" is done with an XML-based declaration language. "Actions" encapsulate project and project item manipulation as well as code generation from the previously mentioned templates. Wizards organize it all for the user. Declarations in an xml configuration file add commands to the familiar project and item context menus. It's completely extensible, too.

Getting into it is more difficult than it should be, since the available labs aren't from the final version of the product and don't always match the sample files you're supposed to manipulate. Some of the best stuff -- the actions to generate and insert code from templates -- isn't documented at all. But it's worth working with. It brings the promise of integrating your architecture standards and your preferred formats for boilerplate code into the Visual Studio environment for others to use.