Skip to content

Instantly share code, notes, and snippets.

@matthewrdev
Last active August 25, 2022 23:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewrdev/6b6bd58597f7604856f274c3a0b99872 to your computer and use it in GitHub Desktop.
Save matthewrdev/6b6bd58597f7604856f274c3a0b99872 to your computer and use it in GitHub Desktop.
A Xamarin.Forms date picker implementation that allows for nullable DateTime values.
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