Last active
July 8, 2024 16:09
-
-
Save jrgcubano/4fdd6ec776c6ddeacb4d518b0355dd77 to your computer and use it in GitHub Desktop.
ExtendedDatePicker with Nullable values for Xamarin.Forms
This file contains hidden or 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 UIKit; | |
using Xamarin.Forms; | |
namespace Check.iOS.Extensions | |
{ | |
/// <summary> | |
/// Color extensions | |
/// </summary> | |
public static class CheckColorExtensions | |
{ | |
internal static readonly UIColor SeventyPercentGrey = new UIColor(0.7f, 0.7f, 0.7f, 1); | |
public static bool IsDefault(this Color color) => Color.Default == color; | |
} | |
} |
This file contains hidden or 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
// Example usage | |
<controls:ExtendedDatePicker | |
x:Name="ContactBirthDate" | |
Placeholder="{i18n:Translate Contact_BirthdayLabel}" | |
NullableDate="{Binding Contact.BirthDate}" | |
Format="d MMM yyyy" | |
XAlign="Start" | |
HasBorder="false" | |
HorizontalOptions="FillAndExpand" | |
StyleId="BirthDateFieldId" | |
VerticalOptions="Center" | |
IsEnabled="{Binding IsNotBusy}" > | |
</controls:ExtendedDatePicker> |
This file contains hidden or 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 Xamarin.Forms; | |
namespace Check.Client.Core.Controls | |
{ | |
/// <summary> | |
/// Extended DatePicker for Nullable Values | |
/// Via: https://forums.xamarin.com/discussion/20028/datepicker-possible-to-bind-to-nullable-date-value | |
/// Via: https://github.com/XLabs/Xamarin-Forms-Labs/wiki/ExtendedEntry | |
/// </summary> | |
public class ExtendedDatePicker : DatePicker | |
{ | |
/// <summary> | |
/// The font property | |
/// </summary> | |
public static readonly BindableProperty FontProperty = | |
BindableProperty.Create("Font", typeof(Font), typeof(ExtendedDatePicker), new Font()); | |
/// <summary> | |
/// The NullableDate property | |
/// </summary> | |
public static readonly BindableProperty NullableDateProperty = | |
BindableProperty.Create("NullableDate", typeof (DateTime?), typeof (ExtendedDatePicker), null, BindingMode.TwoWay); | |
/// <summary> | |
/// The XAlign property | |
/// </summary> | |
public static readonly BindableProperty XAlignProperty = | |
BindableProperty.Create("XAlign", typeof(TextAlignment), typeof(ExtendedDatePicker), | |
TextAlignment.Start); | |
/// <summary> | |
/// The HasBorder property | |
/// </summary> | |
public static readonly BindableProperty HasBorderProperty = | |
BindableProperty.Create("HasBorder", typeof(bool), typeof(ExtendedDatePicker), true); | |
/// <summary> | |
/// The Placeholder property | |
/// </summary> | |
public static readonly BindableProperty PlaceholderProperty = | |
BindableProperty.Create("Placeholder", typeof (string), typeof (ExtendedDatePicker), string.Empty, BindingMode.OneWay); | |
/// <summary> | |
/// The PlaceholderTextColor property | |
/// </summary> | |
public static readonly BindableProperty PlaceholderTextColorProperty = | |
BindableProperty.Create("PlaceholderTextColor", typeof(Color), typeof(ExtendedDatePicker), Color.Default); | |
/// <summary> | |
/// Gets or sets the Font | |
/// </summary> | |
public Font Font | |
{ | |
get { return (Font)GetValue(FontProperty); } | |
set { SetValue(FontProperty, value); } | |
} | |
/// <summary> | |
/// Get or sets the NullableDate | |
/// </summary> | |
public DateTime? NullableDate | |
{ | |
get { return (DateTime?) GetValue(NullableDateProperty); } | |
set | |
{ | |
if (value != NullableDate) | |
{ | |
SetValue(NullableDateProperty, value); | |
UpdateDate(); | |
} | |
} | |
} | |
/// <summary> | |
/// Gets or sets the X alignment of the text | |
/// </summary> | |
public TextAlignment XAlign | |
{ | |
get { return (TextAlignment)GetValue(XAlignProperty); } | |
set { SetValue(XAlignProperty, value); } | |
} | |
/// <summary> | |
/// Gets or sets if the border should be shown or not | |
/// </summary> | |
public bool HasBorder | |
{ | |
get { return (bool)GetValue(HasBorderProperty); } | |
set { SetValue(HasBorderProperty, value); } | |
} | |
/// <summary> | |
/// Get or sets the PlaceHolder | |
/// </summary> | |
public string Placeholder | |
{ | |
get { return (string) GetValue(PlaceholderProperty); } | |
set { SetValue(PlaceholderProperty, value); } | |
} | |
/// <summary> | |
/// Sets color for placeholder text | |
/// </summary> | |
public Color PlaceholderTextColor | |
{ | |
get { return (Color)GetValue(PlaceholderTextColorProperty); } | |
set { SetValue(PlaceholderTextColorProperty, value); } | |
} | |
protected override void OnBindingContextChanged() | |
{ | |
base.OnBindingContextChanged(); | |
UpdateDate(); | |
} | |
protected override void OnPropertyChanged(string propertyName = null) | |
{ | |
base.OnPropertyChanged(propertyName); | |
//Device.OnPlatform(() => | |
//{ | |
if (propertyName == IsFocusedProperty.PropertyName) | |
{ | |
if (IsFocused) | |
{ | |
if (!NullableDate.HasValue) | |
{ | |
Date = (DateTime) DateProperty.DefaultValue; | |
} | |
} | |
else | |
{ | |
OnPropertyChanged(DateProperty.PropertyName); | |
} | |
} | |
//}); | |
if (propertyName == DateProperty.PropertyName) | |
{ | |
NullableDate = Date; | |
} | |
if (propertyName == NullableDateProperty.PropertyName) | |
{ | |
if (NullableDate.HasValue) | |
{ | |
Date = NullableDate.Value; | |
} | |
} | |
} | |
private void UpdateDate() | |
{ | |
if (NullableDate.HasValue) | |
{ | |
Date = NullableDate.Value; | |
} | |
else | |
{ | |
Date = (DateTime)DateProperty.DefaultValue; | |
} | |
} | |
} | |
} |
This file contains hidden or 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 Android.Views; | |
using Check.Client.Core.Controls; | |
using Check.Droid.Renderers; | |
using System.ComponentModel; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Platform.Android; | |
[assembly: ExportRenderer(typeof(ExtendedDatePicker), typeof(ExtendedDatePickerRenderer))] | |
namespace Check.Droid.Renderers | |
{ | |
/// <summary> | |
/// Extended DatePicker Renderer for Nullable Values | |
/// Via: https://forums.xamarin.com/discussion/20028/datepicker-possible-to-bind-to-nullable-date-value | |
/// Via: https://github.com/XLabs/Xamarin-Forms-Labs/wiki/ExtendedEntry | |
/// </summary> | |
public class ExtendedDatePickerRenderer : DatePickerRenderer | |
{ | |
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e) | |
{ | |
base.OnElementChanged(e); | |
var view = Element as ExtendedDatePicker; | |
if (view != null) | |
{ | |
SetFont(view); | |
SetTextAlignment(view); | |
// SetBorder(view); | |
SetNullableText(view); | |
SetPlaceholder(view); | |
SetPlaceholderTextColor(view); | |
} | |
} | |
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
base.OnElementPropertyChanged(sender, e); | |
var view = (ExtendedDatePicker)Element; | |
if (e.PropertyName == ExtendedDatePicker.FontProperty.PropertyName) | |
SetFont(view); | |
else if (e.PropertyName == ExtendedDatePicker.XAlignProperty.PropertyName) | |
SetTextAlignment(view); | |
// else if (e.PropertyName == ExtendedDatePicker.HasBorderProperty.PropertyName) | |
// SetBorder(view); | |
else if (e.PropertyName == ExtendedDatePicker.NullableDateProperty.PropertyName) | |
SetNullableText(view); | |
else if (e.PropertyName == ExtendedDatePicker.PlaceholderProperty.PropertyName) | |
SetPlaceholder(view); | |
else if (e.PropertyName == ExtendedDatePicker.PlaceholderTextColorProperty.PropertyName) | |
SetPlaceholderTextColor(view); | |
} | |
/// <summary> | |
/// Sets the text alignment. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetTextAlignment(ExtendedDatePicker view) | |
{ | |
switch (view.XAlign) | |
{ | |
case Xamarin.Forms.TextAlignment.Center: | |
Control.Gravity = GravityFlags.CenterHorizontal; | |
break; | |
case Xamarin.Forms.TextAlignment.End: | |
Control.Gravity = GravityFlags.End; | |
break; | |
case Xamarin.Forms.TextAlignment.Start: | |
Control.Gravity = GravityFlags.Start; | |
break; | |
} | |
} | |
/// <summary> | |
/// Sets the font. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetFont(ExtendedDatePicker view) | |
{ | |
if (view.Font != Font.Default) | |
{ | |
Control.TextSize = view.Font.ToScaledPixel(); | |
Control.Typeface = view.Font.ToExtendedTypeface(Context); | |
} | |
} | |
/// <summary> | |
/// Set text based on nullable value | |
/// </summary> | |
/// <param name="view"></param> | |
private void SetNullableText(ExtendedDatePicker view) | |
{ | |
if (view.NullableDate == null) | |
Control.Text = string.Empty; | |
} | |
/// <summary> | |
/// Set the placeholder | |
/// </summary> | |
/// <param name="view"></param> | |
private void SetPlaceholder(ExtendedDatePicker view) | |
{ | |
Control.Hint = view.Placeholder; | |
} | |
/// <summary> | |
/// Sets the color of the placeholder text. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetPlaceholderTextColor(ExtendedDatePicker view) | |
{ | |
if (view.PlaceholderTextColor != Color.Default) | |
{ | |
Control.SetHintTextColor(view.PlaceholderTextColor.ToAndroid()); | |
} | |
} | |
} | |
} |
This file contains hidden or 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 Xamarin.Forms.Platform.iOS; | |
using Xamarin.Forms; | |
using Check.Client.Core.Controls; | |
using System.ComponentModel; | |
using UIKit; | |
using Check.iOS.Renderers; | |
using Check.iOS.Extensions; | |
using Foundation; | |
using System; | |
using CoreGraphics; | |
[assembly: ExportRenderer(typeof(ExtendedDatePicker), typeof(ExtendedDatePickerRenderer))] | |
namespace Check.iOS.Renderers | |
{ | |
/// <summary> | |
/// Extended DatePicker Renderer for Nullable Values | |
/// Via: https://forums.xamarin.com/discussion/20028/datepicker-possible-to-bind-to-nullable-date-value | |
/// Via: https://github.com/XLabs/Xamarin-Forms-Labs/wiki/ExtendedEntry | |
/// </summary> | |
public class ExtendedDatePickerRenderer : DatePickerRenderer | |
{ | |
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e) | |
{ | |
base.OnElementChanged(e); | |
var view = Element as ExtendedDatePicker; | |
if (view != null) | |
{ | |
SetFont(view); | |
SetTextAlignment(view); | |
SetBorder(view); | |
SetNullableText(view); | |
SetPlaceholderTextColor(view); | |
ResizeHeight(); | |
} | |
} | |
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
base.OnElementPropertyChanged(sender, e); | |
var view = (ExtendedDatePicker)Element; | |
if (e.PropertyName == ExtendedDatePicker.FontProperty.PropertyName) | |
SetFont(view); | |
else if (e.PropertyName == ExtendedDatePicker.XAlignProperty.PropertyName) | |
SetTextAlignment(view); | |
else if (e.PropertyName == ExtendedDatePicker.HasBorderProperty.PropertyName) | |
SetBorder(view); | |
else if (e.PropertyName == ExtendedDatePicker.NullableDateProperty.PropertyName) | |
SetNullableText(view); | |
else if (e.PropertyName == ExtendedDatePicker.PlaceholderTextColorProperty.PropertyName) | |
SetPlaceholderTextColor(view); | |
ResizeHeight(); | |
} | |
/// <summary> | |
/// Sets the text alignment. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetTextAlignment(ExtendedDatePicker view) | |
{ | |
switch (view.XAlign) | |
{ | |
case TextAlignment.Center: | |
Control.TextAlignment = UITextAlignment.Center; | |
break; | |
case TextAlignment.End: | |
Control.TextAlignment = UITextAlignment.Right; | |
break; | |
case TextAlignment.Start: | |
Control.TextAlignment = UITextAlignment.Left; | |
break; | |
} | |
} | |
/// <summary> | |
/// Sets the font. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetFont(ExtendedDatePicker view) | |
{ | |
UIFont uiFont; | |
if (view.Font != Font.Default && (uiFont = view.Font.ToUIFont()) != null) | |
Control.Font = uiFont; | |
else if (view.Font == Font.Default) | |
Control.Font = UIFont.SystemFontOfSize(17f); | |
} | |
/// <summary> | |
/// Sets the border. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetBorder(ExtendedDatePicker view) | |
{ | |
Control.BorderStyle = view.HasBorder ? UITextBorderStyle.RoundedRect : UITextBorderStyle.None; | |
} | |
/// <summary> | |
/// Set text based on nullable value | |
/// </summary> | |
/// <param name="view"></param> | |
private void SetNullableText(ExtendedDatePicker view) | |
{ | |
if (view.NullableDate == null) | |
Control.Text = string.Empty; | |
} | |
/// <summary> | |
/// Resizes the height. | |
/// </summary> | |
private void ResizeHeight() | |
{ | |
if (Element.HeightRequest >= 0) return; | |
var height = Math.Max(Bounds.Height, | |
new UITextField { Font = Control.Font }.IntrinsicContentSize.Height) * 2; | |
Control.Frame = new CGRect(0.0f, 0.0f, (nfloat)Element.Width, (nfloat)height); | |
Element.HeightRequest = height; | |
} | |
/// <summary> | |
/// Sets the color of the placeholder text. | |
/// </summary> | |
/// <param name="view">The view.</param> | |
private void SetPlaceholderTextColor(ExtendedDatePicker view) | |
{ | |
if (!string.IsNullOrEmpty(view.Placeholder)) | |
{ | |
var foregroundUIColor = view.PlaceholderTextColor.ToUIColor(CheckColorExtensions.SeventyPercentGrey); | |
var backgroundUIColor = view.BackgroundColor.ToUIColor(); | |
var targetFont = Control.Font; | |
Control.AttributedPlaceholder = new NSAttributedString(view.Placeholder, targetFont, foregroundUIColor, backgroundUIColor); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Does anyone ever use this Control? I have a fundamental issue with it and nobody else seems to have that problem.
In ExtendedDatePicker OnBindingContextChanged UpdateDate is called.
UpdateDate checks the NullableDate property and if that is Null it updates the underlying DatePicker's Date property to min date of 01/01/1900.
Setting of the Date property triggers an OnPropertyChnaged in ExtendedDatePicker that then sets the NullableDate to the newly updated Date of 01/01/1900 therefore removing the null Date!
This means you never have a NullableDate that is Null after the first pass of the Binding code. Surely other people have seen this issue too