I’ve used basic data binding in WinForms 2.0 a couple of times, but encountered something new to me this past week. We have a form that has a DateTimePicker control (including ShowCheckBox=true) and want to bind it to a Nullable object property. The requirement here is that the data value is optional for the user to enter (say, tracking a customer’s Birthday) – so if there is no date entered, we want our data value to have null (and therefore, the DB has NULL).
We can’t setup standard databinding on the DataBindings collection …
dtBirthdayNullable.DataBindings.Clear();
dtBirthdayNullable.DataBindings.Add( “Value”, person, “BirtdayNullable” );
…but this throws an exception – since the DateTimePicker.Value property can’t be null.
From here I know that we need to control the data going into, and coming out of, the DateTimePicker control during the data binding process. This is where the Format and Parse events come in handy. The Format event lets you control how you handle data coming out of the object and going into the control property. The Parse event allows you to control how you deal with data coming out of the control before it gets set on your object property.
We setup the DataBindings using a Binding object, so we can hookup the event handlers:
Binding b = new Binding( “Value”, person, “BdayNullable”, true );
dtBirthdayNullable.DataBindings.Add( b );
b.Format += new ConvertEventHandler( dtBirthdayNullable_Format );
b.Parse += new ConvertEventHandler( dtBirthdayNullable_Parse );
Of special importance is the 4th parameter on the Binding constructor. This is to set formattingEnabled, so that we get the format event on the way in to the control.
Finally, the event handlers, and the trick in the Format method:
// OBJECT PROPERTY –> CONTROL VALUE
void dtBirthdayNullable_Format( object sender, ConvertEventArgs e )
{
// e.Value is the object value, we format it to be what we want to show up in the control
Binding b = sender as Binding;
if ( b != null )
{
DateTimePicker dtp = (b.Control as DateTimePicker);
if ( dtp != null )
{
if ( e.Value == null )
{
dtp.ShowCheckBox = true;
dtp.Checked = false;
// have to set e.Value to SOMETHING, since it’s coming in as NULL
// if i set to DateTime.Today, and that’s DIFFERENT than the control’s current
// value, then it triggers a CHANGE to the value, which CHECKS the box (not ok)
// the trick – set e.Value to whatever value the control currently has.
// This does NOT cause a CHANGE, and the checkbox stays OFF.
e.Value = dtp.Value;
}
else
{
dtp.ShowCheckBox = true;
dtp.Checked = true;
// leave e.Value unchanged – it’s not null, so the DTP is fine with it.
}
}
}
}
The key above is that we have to set the e.Value property to something (since the DateTimePicker control is not happy with a null value), but if we set it to something different than what it already is, the control interprets this just like the user changed the value, and therefore sets the checkmark (opposite of what we want/need).
So the trick is that we’ll set e.Value to the what the DateTimePicker.Value already has.
Finish up with the Parse method, which will handle the data coming out of the control and into our object Property. Pretty straight forward:
// CONTROL VALUE –> OBJECT PROPERTY
void dtBirthdayNullable_Parse( object sender, ConvertEventArgs e )
{
// e.value is the formatted value coming from the control.
// we change it to be the value we want to stuff in the object.
Binding b = sender as Binding;
if ( b != null )
{
DateTimePicker dtp = (b.Control as DateTimePicker);
if ( dtp != null )
{
if ( dtp.Checked == false )
{
dtp.ShowCheckBox = true;
dtp.Checked = false;
e.Value = new Nullable();
}
else
{
DateTime val = Convert.ToDateTime( e.Value );
e.Value = new Nullable( val );
}
}
}
}
awsome, thank you for the code and the walkthrough.
In VB 2008, I find the following works for me on nullable date/time fields:
Private Sub BindThis()
Me.dtBirthdayNullable.DataBindings.Clear()
Me.dtBirthdayNullable.DataBindings.Add(“Value”, person, “BdayNullable”, True, DataSourceUpdateMode.OnPropertyChanged, Now()) ‘Now() is the default if empty – change to whatever
If IsDBNull(person.BdayNullable) _
Or person.BdayNullable Is Nothing Then
Me.dtBirthdayNullable.CustomFormat = ” ” ‘This blanks out the contents of the DateTimePicker so it “shows” as having chosen nothing yet
Me.dtBirthdayNullable.Checked = False
Else
Me.dtBirthdayNullable.CustomFormat = “ddd dd/MM/yyyy” ‘or whatever format you wish
End If
End Sub
‘Then just add an event for when the user selects the dateTimePicker so that they see a date on control enter:
Private Sub dtBirthdayNullable_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles dtBirthdayNullable.Enter
If Me.dtBirthdayNullable.CustomFormat = ” ” Then Me.dtBirthdayNullable.CustomFormat = “ddd dd/MM/yyyy”
End Sub
FYI
error CS0118: ‘System.Nullable’ is a ‘type’ but is used like a ‘variable’
error CS0712: Cannot create an instance of the static class ‘System.Nullable
But give an error at line : e.Value = new Nullable( val );
and
e.Value = new Nullable();
Error : “Cannot create an instance of the static class ‘System.Nullable”
Any solution/
arindam:
Here’s the solution:
DateTime val = Convert.ToDateTime(e.Value);
e.Value = new Nullable(val);
Good job!
@Nocompile @arindam
e.Value = new Nullable();
and
e.Value = new Nullable(val);
Cheers
Flo
Crapy http tags…
@Nocompile @arindam
e.Value = new Nullable<DateTime>();
e.Value = new Nullable<DateTime>(val);
The solution as far as I understand is to use not the class but the structure:
e.Value = new Nullable(val);
Oops my brackets were lost. It should be Nullable geenric structure: http://msdn.microsoft.com/en-us/library/kwxxazwb(v=vs.100).aspx
That is something like new Nullable[val]; where [] is for angle brackets.
Odd, I posted the correct code but it got altered. Here is a second attempt with spaces added to preserve formatting:
DateTime val = Convert.ToDateTime(e.Value);
e.Value = new Nullable (val);
Okay, I see the issue. The forum is reformatting the actual code. To get around that i will substitute characters.
Lower case v with underscores (_v_) will be used as the less-than/left pointing character.
Upper case V with underscores (_V_) will be used as the greater-than/right pointing character.
DateTime val = Convert.ToDateTime(e.Value);
e.Value = new Nullable_v_DateTime_V_(val);
Hope this posts.