Binding DateTime and TimeSpan to one property

As we know together, it take different properties to record what user input into DatePicker and TimePicker. DatePicker requires DateTime property, and yet TimePicker needs TimeSpan. And let say we have some kind of application form to submit bunch of data, and one property require the user to input complete set of time, from year to hour but we only have one DateTime property to bind into. Of course we can just add TimeSpan to DateTime and we gonna have complete set of DateTime from our DatePicker and TimePicker. But, what if we got bunch of DateTime property to input, we gonna adding TimeSpan to DateTime multiple times, which is ineffective. So, I create a simple custom control to simplify this problem. Let’s take a look.

XAML File

First, we need to create new content view with XAML on it. In XAML file we gonna need three 2 visible items, the DatePicker and TimePicker, and one invisible item, a DatePicker. The function of two first properties is obvious, to enable user input. But the last invisible DateTime will serve special function. I will explain later after we reach the code behind section, for now let see the code for XAML file

TitledDateTimePickerCode2

As you can see, I have one invisible DatePicker that bind to property called SelectedDateTime, we will use this property to bind whatever Date and Time that user input. One thing you need to take note is how I handle property changed in both visible DatePicker and TimePicker. Whenever user change the date or time, it will also change the our main property, SelectedDateTime.

Another thing you may notice is, I’m using custom Custom Date and Time Picker. It’s actually just custom picker with adjustable Font Size. If you curious, you can take a look at my previous post about adjusting font size of Xamarin Form’s Picker. But of course you also can use the original DatePicker and TimePicker from Xamarin Forms. It will works just fine, you just can’t bind the font size.

Code Behind

Now let’s move to the code behind that XAML file. This file actually is just full of properties you wanna bind to this custom control. You can add more properties that fits your needs, like FontColor maybe, or any other properties. But I wanna focus to how I handle property changes in this file. Like I said before, I use DateSelected and PropertyChanged¬†event handler in DatePicker and TimePicker respectively to update SelectedDateTime. But this method has one weakness. If the SelectedDateTime already has value when its bind, the visible DatePicker and TimePicker won’t show it’s value. Before we go any further, let’s take a look at the code.

public partial class TitledDateTimePicker : ContentView
    {
        public TitledDateTimePicker()
        {
            InitializeComponent();
            LabelTitle.BindingContext = this;
            DatePickerContent.BindingContext = this;
            TimePickerContent.BindingContext = this;
        }

        public static BindableProperty SelectedDateTimeProperty =
            BindableProperty.Create(nameof(SelectedDateTime), typeof(DateTime?), typeof(TitledDateTimePicker), DateTime.Now, BindingMode.TwoWay);

        public DateTime? SelectedDateTime
        {
            get => (DateTime?)GetValue(SelectedDateTimeProperty);
            set => SetValue(SelectedDateTimeProperty, value);
        }

        public static BindableProperty TitleProperty =
            BindableProperty.Create(nameof(Title), typeof(string), typeof(TitledDateTimePicker), null, BindingMode.TwoWay);

        public string Title
        {
            get => (string)GetValue(TitleProperty);
            set => SetValue(TitleProperty, value);
        }

        public static BindableProperty DatetimeProperty =
            BindableProperty.Create(nameof(Datetime), typeof(DateTime), typeof(TitledDateTimePicker), DateTime.Today, BindingMode.TwoWay);

        public DateTime Datetime
        {
            get => (DateTime)GetValue(DatetimeProperty);
            set => SetValue(DatetimeProperty, value);
        }

        public static BindableProperty TimespanProperty =
            BindableProperty.Create(nameof(Timespan), typeof(TimeSpan), typeof(TitledDateTimePicker), DateTime.Today.TimeOfDay, BindingMode.TwoWay);

        public TimeSpan Timespan
        {
            get => (TimeSpan)GetValue(TimespanProperty);
            set => SetValue(TimespanProperty, value);
        }

        public static BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), typeof(int), typeof(TitledDateTimePicker), 12, BindingMode.TwoWay);

        public int FontSize
        {
            get => (int)GetValue(FontSizeProperty);
            set => SetValue(FontSizeProperty, value);
        }

        public static BindableProperty IsEditableProperty =
            BindableProperty.Create(nameof(IsEditable), typeof(bool), typeof(TitledEntry), true, BindingMode.TwoWay);

        public bool IsEditable
        {
            get => (bool)GetValue(IsEditableProperty);
            set => SetValue(IsEditableProperty, value);
        }

        void Handle_DateSelected(object sender, DateChangedEventArgs e)
        {
            UpdateSelectedDatime();
        }

        void Handle_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Time")
            {
                UpdateSelectedDatime();
            }
        }

        void Handle_BindingContextChanged(object sender, EventArgs e)
        {
            if (SelectedDateTime.HasValue)
                SetDateTime();
        }

        void UpdateSelectedDatime()
        {
            SelectedDateTime = Datetime.Add(Timespan);
        }

        void SetDateTime()
        {
            Datetime = SelectedDateTime.Value;
            Timespan = SelectedDateTime.Value.TimeOfDay;
        }
    }

As you can see at code above, whenever DateSelected and PropertyChanged fired, means user input date or time, it will update SelectedDateTime. In other hand, BindingContextChanged of my invisible DatePicker do the completely opposites. It will update the DateTime and TimeSpan of the visible DatePicker and TimePicker respectively.

So, when user navigate to a page which use this custom control, and he want to update a DateTime field that has beed set before hand, he will find fields of DatePicker and TimePicker that already set to proper values, because when he navigate to that page, the BindingContextChanged will be triggered. If we don’t do this, the DatePicker will set to default value, which is January 1st, 1990. This why we need that invisible DatePicker, because we need to put that BindingContextChanged event handler somewhere in the code.

How to use it?

Nah, we already have packed up everything we need about Date and Time Selecting in one simple custom control. So, all we need to do is calling it from any pages that need it. This is a simple sample how to do it.

TitledDateTimePickerCode1

 

Sample Code is available in my Github repo

 

Advertisements

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s