Thursday, January 10, 2019

My Nullable DateTime Picker

I needed to bind a nullable DateTime to a Winforms DateTimePicker control. Unfortunately, the standard DateTimePicker only supports regular DateTime objects. I looked around and found some controls that other people had built, but none of them did exactly what I needed. 

I wanted a control where, if the checkbox is unchecked, the value is considered null and the box is blanked out. I couldn’t find one like that, so I built my own.

The first one I built was written in VB.Net. In this blog post, we'll create a C# version. 


We start by creating a new class and having it inherit from DateTimePicker. 
using System.Windows.Forms;

namespace NullableSample
{
    public class NullableDateTimePicker : DateTimePicker
    {
    }
}

Then add a nullable DateTime property to hold the binded value. 
public class NullableDateTimePicker : DateTimePicker
{
    public DateTime? BindedValue { get; set; } 
}
In the constructor, we set the ShowCheckBox value to true because we want it to always be visible. Then we set the format type to custom. 
public NullableDateTimePicker() : base()
{
    this.ShowCheckBox = true;
    this.Format = DateTimePickerFormat.Custom; 
}
Now we create a method to use to bind our nullable value to our property: 
public void Bind(object datasource, string dataproperty)
{
    var oldBinding = this.DataBindings["BindedValue"];
    if (oldBinding != null)
        this.DataBindings.Remove(oldBinding);

    var b = new Binding("BindedValue", datasource, dataproperty, true);
    b.Format += FormatMyDate;
    b.Parse += ParseMyDate;
    this.DataBindings.Add(b);
}
Notice we also add handlers for the Format and Parse events. 

In the Format handler, we check to see if the date is null. If it is, we set the custom format to be a blank string and un-check the check box. Otherwise, if there is a date, we set the custom format to MM/dd/yyyy and the check box to checked. 
private void FormatMyDate(object sender, ConvertEventArgs e)
{
    if (e.Value == null)
    {
        this.Value = this.Value;
        this.Format = DateTimePickerFormat.Custom;
        this.CustomFormat = " ";
        this.Checked = false;
    }
    else
    {
        this.Value = (DateTime)e.Value;
        this.Format = DateTimePickerFormat.Custom;
        this.CustomFormat = "MM/dd/yyyy";
        this.Checked = true;
    }
}
The Parse handler checks to see if the checkbox is checked. If it is, is sets the value. Otherwise is sets the value to null.
private void ParseMyDate(object sender, ConvertEventArgs e)
{
    if (this.Checked == true)
        e.Value = this.Value;
    else
        e.Value = null;
}
Finally we need to override the OnValueChanged event. Here we set the BindedValue property to the value that was entered. Then, if the checkbox is checked, we set the custom format to MM/dd/yyyy. If it’s not, we make it blank.
protected override void OnValueChanged(EventArgs eventargs)
{
    base.OnValueChanged(eventargs);

    BindedValue = this.Value;

    this.Format = DateTimePickerFormat.Custom;

    if (this.Checked == false)
        this.CustomFormat = " ";
    else
        this.CustomFormat = "MM/dd/yyyy";
}

To test it out, we'll create a simple Person class with a nullable DateTime field as the date of birth: 
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? DOB { get; set; }
    }

Next, we'll compile the project and add a NullableDateTimePicker to our form. In our code we create an instance of the Person class and bind its DOB field to our nullable datetime picker: 

   var tim = new Person { FirstName = "Tim", LastName = "Listman", DOB = new DateTime(1980, 1, 30) };

   nullableDateTimePicker1.Bind(tim, "DOB"); 
If we run it now, it should look like this:



If the box is un-checked, nullableDateTimePicker1.BindedValue.HasValue will return false.

But if the box is checked, we can get the selected date from nullableDateTimePicker1.BindedValue.Value. 


1 comment:

kris said...

Hello,
This custom binding does not fire OnPropertyChanged and that is what i need
Can you see the solution ?