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

 

Set Placeholder Color of Xamarin’s Picker

In one of my previous post, we’ve discussed about how to adjust font size of Xamarin’s Picker. Font size in picker, especially in Android picker, is simply fixed, so we need a workaround if we want to adjust it. In iOS, font size of the picker is more flexible, you can set height request and width request, or simply put it on a grid, the size of whole picker will follow its container. But there’s another property of picker that also fixed. It’s the placeholder color. I know, most of the time we won’t need to change the placeholder color of the picker. However, for the sake of the app’s theme, sometime we gonna need to set color of the placeholder. So, In case you need it, you can use this method to set the placeholder color.

Custom Picker

To set the placeholder color, first thing we gonna need, of course, a bindable property for it. So we gonna update our CustomPicker from previous post and add Placeholder Color property in it

 

public class CustomPicker : Picker
{
    public static readonly BindableProperty FontSizeProperty =
        BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker), 24, BindingMode.TwoWay);

    public Int32 FontSize
    {
        set { SetValue(FontSizeProperty, value); }
        get { return (Int32)GetValue(FontSizeProperty); }
    }

    public static BindableProperty PlaceholderColorProperty =
        BindableProperty.Create(nameof(PlaceholderColor), typeof(string), typeof(CustomPicker), "#CCCCCC", BindingMode.TwoWay);

    public string PlaceholderColor
    {
        get { return (string)GetValue(PlaceholderColorProperty); }
        set { SetValue(PlaceholderColorProperty, value); }
    }
}

 

Custom Renderer Android

Next, we update the renderer in Android. In Android, representing the placeholder color, we have property called Hint Text Color. The only problem is Hint Text Color use Color class from Android.Graphic class unlike default Color of Xamarin from Xamarin.Form class. As a result, we need to parse the color. Fortunately, Android.Graphic.Color take hex code as its parameter so we can just simply parse it. The following code is how we do it.

public class CustomPickerRenderer : PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.CustomPickerBackground);
        Control?.SetPadding(20, 20, 20, 20);
        if (e.OldElement != null || e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;
            Control.TextSize *= (customPicker.FontSize * 0.01f);
            Control.SetHintTextColor(Android.Graphics.Color.ParseColor(customPicker.PlaceholderColor));
        }
    }
}

Custom Renderer iOS

Nah, it’s where things got tricky. To set placeholder color, or in iOS term, Foreground Color, we gonna need class named UIColor. And UIColor doesn’t take Hex Code as its parameter. It takes RGB color. Consequently, we need to parse Hex to RGB first. I made three little functions to take the color Red, Green and Blue from our Hex Code.

Finish with the color thing, next we gonna put that color into something, called NSAttributedString. So, what is NSAttributedString? First, Placeholder Attribute in iOS Picker is a NSAttributedString, so we need to make our own NSAttributedString if we want to modify our placeholder. Second, NSAttributedString has many attributes that you’re able to modify. In this example we just use the Foreground Color. but it has many more important attributes such as Background Color , Font, and Stroke Color.

So, this is how the custom renderer in iOS looks like.

public class CustomPickerRenderer: PickerRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null || e.NewElement != null)
        {
            var customPicker = e.NewElement as CustomPicker;

            string placeholderColor = customPicker.PlaceholderColor;
            UIColor color = UIColor.FromRGB(GetRed(placeholderColor), GetGreen(placeholderColor), GetBlue(placeholderColor));

            var placeholderAttributes = new NSAttributedString(customPicker.Title,new UIStringAttributes()
            { ForegroundColor = color });

            Control.AttributedPlaceholder = placeholderAttributes;
        }
    }

    private float GetRed(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.R;
    }

    private float GetGreen(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.G;
    }

    private float GetBlue(string color)
    {
        Color c = Color.FromHex(color);
        return (float)c.B;
    }
}

Let’s try it out!

I made minor change to example I used in previous post. I add placeholder color and run it in Android and iOS emulator.


    
        
        
        
        
    

 

Sample Code is available in my Github repo.

Adjusting font size of Xamarin’s Picker

When you’re developing the front-end side of a Xamarin’s app, you’re demanded to put and arrange every UI component in the right place, with right size and right color.¬† When it comes to size, it’s kinda troublesome to adjust picker to precise font size. The font size of picker in Xamarin, including DatePicker and TimePicker, is simply fixed. So, in this post, we will discuss about a workaround method to approach this problem. With this method, you will be even able to bind the font size of the picker to certain variable in view model if you want to. Note: so far, it works in Android, I still haven’t tried it in iOS. I’ll update this post or make another post after try it in iOS.

Custom Picker Class

The first step is we will create the custom class of the picker. This custom class will inherit picker class and we’ll add one binding property, font size, to this class. The following code is the example of the custom class.


 public class CustomPicker : Picker
    {
        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), typeof(Int32), typeof(CustomPicker),
                10, BindingMode.Default);
        public Int32 FontSize
        {
            get => (Int32)GetValue(FontSizeProperty);
            set => SetValue(FontSizeProperty, value);
        }
    }

Updating Picker UI

The second step, we move on to the Android project. This part is just about the UI, nothing to do with the functionality of the picker. You can ignore and jump to next step if you feel don’t need it. So, in this part, I made and axml file which define the the background of the picker. As you know, standard Xamarin is just a text with underline, not so eye catching. So, I make little bit modification on it. I put the file in Drawable folder inside Resources.




  
    
      
      
      
    
  

  
    
      
      
      
    
  


Custom Picker Renderer

The third step is the most important part. We gonna make custom renderer of the picker. Inheriting from PickerRenderer class, we will adjust the text size property of the picker base on font size property that we’ve created earlier. I use 0.01 as the multiplier just because it suit my need in my previous project, you can change it to meet your need if you feel need to.


 class CustomPickerRenderer : PickerRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            this.Control.Background = Forms.Context.GetDrawable(Resource.Drawable.customPicker);
            if (e.OldElement != null || e.NewElement != null)
            {
                var customPicker = e.NewElement as CustomPicker;
                Control?.SetPadding(20, 0, 0, 0);
                Control.TextSize *= (customPicker.FontSize * 0.01f);
            }

        }
    }

Implementation

Now let’s try it out. The following codes if the sample of xaml file where I use the custom picker, followed by the view model.




    
        
            
            
            
            
        
    


 public class PickerViewModel : INotifyPropertyChanged
    {

        private List countryList;
        private List stateList;
        private List cityList;

        public List CountryList
        {
            get => countryList;
            set => SetObservableProperty(ref countryList, value);
        }

        public List StateList
        {
            get => stateList;
            set => SetObservableProperty(ref stateList, value);
        }

        public List CityList
        {
            get => cityList;
            set => SetObservableProperty(ref cityList, value);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public PickerViewModel()
        {
            CountryList = new List() { "USA", "China", "Russia" };
            StateList = new List() { "California", "Sichuan", "Kirov" };
            CityList = new List() { "Los Angeles", "Guangzhou", "St. Petersburg" };
        }

        protected void SetObservableProperty(ref T field, T value,
            [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer.Default.Equals(field, value)) return;
            field = value;
            OnPropertyChanged(propertyName);
        }

        protected virtual void OnPropertyChanged(string propertyName)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PickerPage : ContentPage
    {
        public PickerPage()
        {
            InitializeComponent();
            BindingContext = new PickerViewModel();
        }
    }

When you run the project, you will have three different picker with three different size. You can try to bind the font size property with any integer variable you have within your view model.

 

Update : Set Placeholder Color of Xamarin’s Picker

Update : Adjusting Font Size of Xamarin’s Picker (Part 2)

Sample Code is Available in my Github repo