Last active
August 25, 2022 23:58
-
-
Save matthewrdev/6b6bd58597f7604856f274c3a0b99872 to your computer and use it in GitHub Desktop.
A Xamarin.Forms date picker implementation that allows for nullable DateTime values.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Windows.Input; | |
using Xamarin.Essentials; | |
using Xamarin.Forms; | |
namespace MyApp.Controls | |
{ | |
public class NullableDateChangedEventArgs : EventArgs | |
{ | |
public NullableDateChangedEventArgs(DateTime? newValue, DateTime? oldValue) | |
{ | |
NewValue = newValue; | |
OldValue = oldValue; | |
} | |
public DateTime? NewValue { get; } | |
public DateTime? OldValue { get; } | |
} | |
public class NullableDatePicker : DatePicker | |
{ | |
const string defaultDateTimeFormat = "dd MMMM yyyy"; | |
const string defaultPlaceholderLabel = "'Select Date'"; | |
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(NullableDatePicker), defaultPlaceholderLabel, propertyChanged:OnPlaceholderChanged); | |
private static void OnPlaceholderChanged(BindableObject bindable, object oldValue, object newValue) | |
{ | |
var control = bindable as NullableDatePicker; | |
if (control is null) | |
{ | |
return; | |
} | |
control.Apply(control.DateTimeValue, control.TimeFormat, newValue as string); | |
} | |
public string Placeholder | |
{ | |
get => (string)GetValue(PlaceholderProperty); | |
set => SetValue(PlaceholderProperty, value); | |
} | |
public static readonly BindableProperty DateTimeFormatProperty = BindableProperty.Create(nameof(TimeFormat), typeof(string), typeof(NullableDatePicker), defaultDateTimeFormat, propertyChanged: OnDateTimeFormatChanged); | |
private static void OnDateTimeFormatChanged(BindableObject bindable, object oldValue, object newValue) | |
{ | |
var control = bindable as NullableDatePicker; | |
if (control is null) | |
{ | |
return; | |
} | |
control.Apply(control.DateTimeValue, newValue as string, control.Placeholder); | |
} | |
public string TimeFormat | |
{ | |
get => (string)GetValue(DateTimeFormatProperty); | |
set => SetValue(DateTimeFormatProperty, value); | |
} | |
public static readonly BindableProperty DateTimeValueProperty = BindableProperty.Create(nameof(DateTimeValue), typeof(DateTime?), typeof(NullableDatePicker), default(DateTime?), BindingMode.TwoWay, propertyChanged: OnDateTimeValueChanged); | |
private static void OnDateTimeValueChanged(BindableObject bindable, object oldValue, object newValue) | |
{ | |
var control = bindable as NullableDatePicker; | |
if (control is null) | |
{ | |
return; | |
} | |
control.Apply(newValue as DateTime?, control.TimeFormat, control.Placeholder); | |
} | |
public DateTime? DateTimeValue | |
{ | |
get => (DateTime?)GetValue(DateTimeValueProperty); | |
set => SetValue(DateTimeValueProperty, value); | |
} | |
public event EventHandler<NullableDateChangedEventArgs> OnNullableDateChanged; | |
public NullableDatePicker() | |
{ | |
BindEvents(); | |
Apply(DateTimeValue, TimeFormat, Placeholder); | |
} | |
private void BindEvents() | |
{ | |
UnbindEvents(); | |
this.DateSelected += NullableDatePicker_DateSelected; | |
this.Unfocused += NullableDatePicker_Unfocused; | |
} | |
private void UnbindEvents() | |
{ | |
this.DateSelected -= NullableDatePicker_DateSelected; | |
this.Unfocused -= NullableDatePicker_Unfocused; | |
} | |
private void Apply(DateTime? dateTimeValue, string dateTimeFormat, string placeholder) | |
{ | |
try | |
{ | |
UnbindEvents(); | |
if (dateTimeValue.HasValue) | |
{ | |
this.Date = dateTimeValue.Value; | |
Format = dateTimeFormat; | |
} | |
else | |
{ | |
this.Date = DateTime.Today.Date; | |
if (DeviceInfo.Platform == DevicePlatform.UWP) | |
{ | |
// On Windows (Xamarin.Forms 5.0), the placeholder format does not display correctly and stops the control from rendering. | |
// Default to the standard format instead. | |
Format = dateTimeFormat; | |
} | |
else | |
{ | |
Format = string.IsNullOrEmpty(placeholder) ? defaultPlaceholderLabel : placeholder; | |
} | |
} | |
} | |
finally | |
{ | |
BindEvents(); | |
} | |
} | |
~NullableDatePicker() | |
{ | |
UnbindEvents(); | |
} | |
private void NullableDatePicker_Unfocused(object sender, FocusEventArgs e) | |
{ | |
if (!this.DateTimeValue.HasValue) | |
{ | |
DateTimeValue = this.Date; | |
NotifyDateTimeValueChanged(null, DateTimeValue); | |
} | |
} | |
private void NullableDatePicker_DateSelected(object sender, DateChangedEventArgs e) | |
{ | |
NotifyDateTimeValueChanged(DateTimeValue, e.NewDate); | |
} | |
private void NotifyDateTimeValueChanged(DateTime? oldDateValue, DateTime? newDateValue) | |
{ | |
OnNullableDateChanged?.Invoke(this, new NullableDateChangedEventArgs(newDateValue, oldDateValue)); | |
DateTimeValue = newDateValue; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment