Dan's IK Blog

Periodic ramblings about all things .NET

My Links

Blog Stats

Article Categories

Archives

Post Categories

Blogroll

WinForms - DataBinding DateTimePicker to a Nullable Type

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 );

            }

        }

    }

}

posted on Sunday, January 21, 2007 9:17 PM