My blog about application development on the .NET platform and Windows®.

Binding the DatePickerTextBox in WPF

The default control template of the built-in DatePicker control in WPF and Silverlight consists of, among some other visual elements and panels that defines its appearance and internal layout, a Calendar control that lets the user select a date by using a visual calendar and an editable DatePickerTextBox where the currently selected date is displayed.

When you bind the SelectedDate property of a DatePicker to a DateTime source property and entering a new date in the TextBox using the keyboard, the source property don’t actually get set until the TextBox loses focus. This behaviour happens even if you set the UpdateSourceTrigger property of the Binding to PropertyChanged and may cause some issues in your application.

If you for example are performing some validations based on the value of the source property and also are enabling or disabling controls or presenting some visual feedback to the user as a result of this validation logic, you probably want to perform the very same logic regardless of whether the user enters the new date using the calendar – the source property does get set as expected when a date is selected in the calendar – or by entering it directly in the TextBox.

A solution to this issue is to define your own custom DatePicker class that extends the built-in one and handles the TextChanged event of the DatePickerTextBox. You can get a reference to the DatePickerTextBox in the DatePicker’s control template and hook up an event handler for the event by overriding the base class’ OnApplyTemplate() method:

public class CustomDatePicker : DatePicker
{
    protected DatePickerTextBox _datePickerTextBox;
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _datePickerTextBox = this.Template.FindName("PART_TextBox", this) as DatePickerTextBox;
        if (_datePickerTextBox != null)
        {
            _datePickerTextBox.TextChanged += dptb_TextChanged;
        }
    }

    private void dptb_TextChanged(object sender, TextChangedEventArgs e)
    {
       /* implementation presented below */
    }
}

In the TextChanged event handler you can then parse the value of the DatePicker’s Text property using the System.DateTime.TryParseExact method and, providing that the Text property actually contains a valid date, then set the SelectedDate property of the DatePicker:

public class CustomDatePicker : DatePicker
{
    protected DatePickerTextBox _datePickerTextBox;
    protected readonly string _shortDatePattern;

    public CustomDatePicker()
        : base()
    {
        _shortDatePattern = Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern;
    }

    public override void OnApplyTemplate()
    {
        ...
    }


    private void dptb_TextChanged(object sender, TextChangedEventArgs e)
    {
        DateTime dt;
        if (DateTime.TryParseExact(_datePickerTextBox.Text, _shortDatePattern, Thread.CurrentThread.CurrentCulture, 
            System.Globalization.DateTimeStyles.None, out dt))
        {
                this.SelectedDate = dt;
        }

    }
}

An important thing to notice in the above sample code is that the DateTime.TryParseExact method will only return true when the Text property contains a valid and completely entered date that matches the date format of the DatePicker. This format is the same that Windows uses to display dates, unless of course you have explicitly set the Thread.CurrentThread.CurrentCulture property to some other culture somewhere in your application. The default format is set under the Region and Language settings in the ControlPanel:

ControlPanel
This means that a British user has to enter both the month and day part of the new date using two digits for the source property to get set immediately while an American user can choose to enter between 1 or 2 digits for both the day and the month part since Window’s default short date formats for the United Kingdom and United States regions are “dd/MM/yyyy” and “M/d/yyyy” respectively.

Also, for the parsing of the Text property in the TextChanged event handler to work as expected, the SelectedDateFormat of the DatePicker must be set to its default enumeration value of DatePickerFormat.Long.

Sample application

There is a sample project that uses the custom DatePicker class and demonstrates how the source property get set when you enter a date in the TextBox that can be downloaded from the MSDN code gallery here.

The sample WPF application consists of a single window with a CustomDatePicker control and TextBlock element that displays the current value of the same DateTime source property of a view model that is bound to the SelectedDate property of the CustomDatePicker control:

public class ViewModel : INotifyPropertyChanged
{
    private DateTime? _date;
    public DateTime? Date
    {
        get { return _date; }
        set { _date = value; NotifyPropertyChanged(); }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
<Window x:Class="Mm.CustomDatePicker.WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Mm.CustomDatePicker.WpfApplication"
        xmlns:controls="clr-namespace:Mm.CustomDatePicker.Controls;assembly=Mm.CustomDatePicker.Controls"
        Title="blog.magnusmontin.net" Height="150" Width="525">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <DockPanel Margin="20">
        <controls:CustomDatePicker DockPanel.Dock="Top" SelectedDate="{Binding Date}"/>
        
        <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
            <TextBlock Text="Selected Date: " FontSize="16" FontWeight="Bold"/>
            <TextBlock Text="{Binding Date, StringFormat=dd/MM/yyyy}" FontSize="16"/>
        </StackPanel>
    </DockPanel>
</Window>

CustomDatePicker

See Also

DatePicker Class: http://msdn.microsoft.com/en-us/library/system.windows.controls.datepicker(v=vs.110).aspx (MSDN)
Binding.UpdateSourceTrigger Property: http://msdn.microsoft.com/en-us/library/system.windows.data.binding.updatesourcetrigger(v=vs.110).aspx (MSDN)
DateTime.TryParseExact Method: http://msdn.microsoft.com/en-us/library/system.datetime.tryparseexact(v=vs.110).aspx (MSDN)

Advertisements

2 Comments on “Binding the DatePickerTextBox in WPF”

  1. Vinos says:

    thanks a toonnnnnnnn :). you saved a hell lot of work.. Best solution you would ever get.

  2. Vikram says:

    very nice solution


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s