I'm using a nullable DatePicker, which is implemented by subclassing from DatePicker and using custom renderers.
public class ExtendedDatePicker : DatePicker
{
public static readonly BindableProperty NullableDateProperty =
BindableProperty.Create(
"NullableDate",
typeof(DateTime?),
typeof(ExtendedDatePicker),
null,
BindingMode.TwoWay);
public static readonly BindableProperty PlaceholderProperty =
BindableProperty.Create(
"Placeholder",
typeof(string),
typeof(ExtendedDatePicker),
string.Empty,
BindingMode.OneWay);
public DateTime? NullableDate
{
get { return (DateTime?)GetValue(NullableDateProperty); }
set
{
if (value != NullableDate)
{
SetValue(NullableDateProperty, value);
}
}
}
public string Placeholder
{
get { return (string)GetValue(PlaceholderProperty); }
set { SetValue(PlaceholderProperty, value); }
}
public ExtendedDatePicker()
{
//this.Unfocused += ExtendedDatePicker_Unfocused;
//this.DateSelected += ExtendedDatePicker_DateSelected;
}
//private void ExtendedDatePicker_DateSelected(object sender, DateChangedEventArgs e)
//{
// NullableDate = Date;
//}
//private void ExtendedDatePicker_Unfocused(object sender, FocusEventArgs e)
//{
// if (Device.RuntimePlatform == Device.Android && !e.IsFocused)
// {
// NullableDate = Date;
// }
//}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
UpdateDate();
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == IsFocusedProperty.PropertyName)
{
if (IsFocused)
{
if (!NullableDate.HasValue)
{
Date = (DateTime)DateProperty.DefaultValue;
}
}
else
{
OnPropertyChanged(DateProperty.PropertyName);
}
}
if (propertyName == DateProperty.PropertyName)
{
if (Date != default(DateTime))
{
if (Date != NullableDate.GetValueOrDefault())
NullableDate = Date;
}
else
{
if (NullableDate != null)
NullableDate = null;
}
}
if (propertyName == NullableDateProperty.PropertyName)
{
if (NullableDate.HasValue)
{
if (Date != NullableDate.GetValueOrDefault())
Date = NullableDate.Value;
}
}
}
private void UpdateDate()
{
if (NullableDate.HasValue)
{
Date = NullableDate.Value;
}
else
{
Date = (DateTime)DateProperty.DefaultValue;
}
}
}
I don't provide the custom renderers here, because they are mostly setter. My current approach works, but the clearing of the selected date has to be done via an extra button and pressing "cancel" still sets the date in the datepicker. Now I want to improve this behavior and I'm looking for a better solution.
Issues:
- user should be able to select any date from the datepicker (including todays)
- be able to clear the current selected date
- cancellation should behave like a cancellation (and clears the date)
- works for all platforms (iOS, Droid, UWP)
There is already a feature request, but this hasn't been implemented yet. Also there are some tries to workaround this problem, but I haven't found a working solution, which solves all my issues.
Either I can use the DateSelected event, which does not get fired if I select todays date (or the last selected date to be exactly and on initialization this is todays date). Or I set the NullableDate everytime and on cancellation the date is also shown. As result, the user can't clear the date if he press on cancel. I tried to experiment with the Unfocused event, but I haven't found something useful. Perhaps one could use the ViewRenderer and exchanges the underlying native view to have full control over it? But the renderers have wrong base classes ...
Any ideas?