Reputation: 2401
I trying to use .NET MAUI's date picker control in my app. I have a requirement that initially when screen get displayed need to show nothing or "-" below StartDate label. But datepicker control is showing default value like 01/01/1900.
Now, the question is how to modify a datepicker control that it will not show any default value initially?
I have did it using label control above the datepicker inside grid. But that is work around with different control. How to handle this using DatePicker control it self? Is it possible?
With reference to XamGirl's blog post here: https://xamgirl.com/clearable-datepicker-in-xamarin-forms/
And as @Dongzhi Wang-MSFT mentioned we can use Xamarin.Forms renderers in MAUI. So, I have followed the same and created NullDatePiceker control and Custom renderers on both platforms. But it does work only on Android.
using System;
using UIKit;
using Microsoft.Maui.Controls.Handlers.Compatibility;
using Silvar.Mobile.UI.Core.Controls;
using System.ComponentModel;
using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Controls.Platform;
namespace Tyler.EnerGov.Mobile.UI.Core.iOS
{
public class NullableDatePickerRenderer : ViewRenderer<NullableDatePicker, UITextField>
{
protected override void OnElementChanged(ElementChangedEventArgs<NullableDatePicker> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && this.Control != null)
{
this.AddClearButton();
var entry = (NullableDatePicker)this.Element;
if (!entry.NullableDate.HasValue)
{
this.Control.Text = entry.PlaceHolder;
}
if (DeviceInfo.Idiom == DeviceIdiom.Tablet)
{
this.Control.Font = UIFont.SystemFontOfSize(25);
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Check if the property we are updating is the format property
if (e.PropertyName == NullableDatePicker.DateProperty.PropertyName || e.PropertyName == NullableDatePicker.FormatProperty.PropertyName)
{
var entry = (NullableDatePicker)this.Element;
// If we are updating the format to the placeholder then just update the text and return
if (this.Element.Format == entry.PlaceHolder)
{
this.Control.Text = entry.PlaceHolder;
return;
}
}
base.OnElementPropertyChanged(sender, e);
}
private void AddClearButton()
{
var originalToolbar = this.Control.InputAccessoryView as UIToolbar;
if (originalToolbar != null && originalToolbar.Items.Length <= 2)
{
var clearButton = new UIBarButtonItem("Clear", UIBarButtonItemStyle.Plain, ((sender, ev) =>
{
NullableDatePicker baseDatePicker = this.Element as NullableDatePicker;
this.Element.Unfocus();
this.Element.Date = DateTime.Now;
baseDatePicker.CleanDate();
}));
var newItems = new List<UIBarButtonItem>();
foreach (var item in originalToolbar.Items)
{
newItems.Add(item);
}
newItems.Insert(0, clearButton);
originalToolbar.Items = newItems.ToArray();
originalToolbar.SetNeedsDisplay();
}
}
}
}
Here when OnElementChanged() got called and in that getting this.Control as null. I have registered a renderer using AddHandler.
Does anyone have an idea about this?
Upvotes: 1
Views: 2083
Reputation: 2401
I'm able to achieve Nullable Date with a clear button in iOS using Handlers. But now the Done button has stopped working.
public class NullableDatePickerRenderer : DatePickerHandler
{
protected override MauiDatePicker CreatePlatformView()
{
return base.CreatePlatformView();
}
protected override void ConnectHandler(MauiDatePicker platformView)
{
var datePicker = VirtualView as NullableDatePicker;
var platformPicker = new MauiDatePicker();
var originalToolbar = platformPicker.InputAccessoryView as UIToolbar;
if (originalToolbar != null && originalToolbar.Items.Length <= 2)
{
var clearButton = new UIBarButtonItem("Clear", UIBarButtonItemStyle.Plain, ((sender, ev) =>
{
datePicker.Unfocus();
datePicker.Date = DateTime.Now;
datePicker.CleanDate();
}));
var newItems = new List<UIBarButtonItem>();
foreach (var item in originalToolbar.Items)
{
newItems.Add(item);
}
newItems.Insert(0, clearButton);
originalToolbar.Items = newItems.ToArray();
originalToolbar.SetNeedsDisplay();
platformView.InputAccessoryView = originalToolbar;
platformView.ReloadInputViews();
base.ConnectHandler(platformView);
}
}
}
Upvotes: 1
Reputation: 1609
You can use Custom Renderers, like this
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace ClearButtonDemo
{
public class NullableDatePicker : DatePicker
{
public NullableDatePicker()
{
// Format = "d";
}
public event EventHandler ClearRequested;
public void clear()
{
if (ClearRequested != null)
{
ClearRequested(this, EventArgs.Empty);
}
}
public string _originalFormat = null;
public static readonly BindableProperty PlaceHolderProperty =
BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(NullableDatePicker), "");
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProperty); }
set
{
SetValue(PlaceHolderProperty, value);
}
}
public static readonly BindableProperty NullableDateProperty =
BindableProperty.Create(nameof(NullableDate), typeof(DateTime?), typeof(NullableDatePicker), null, defaultBindingMode: BindingMode.TwoWay);
public DateTime? NullableDate
{
get { return (DateTime?)GetValue(NullableDateProperty); }
set { SetValue(NullableDateProperty, value); UpdateDate(); }
}
private void UpdateDate()
{
if (NullableDate != null)
{
if (_originalFormat != null)
{
Format = _originalFormat;
}
}
else{
Format = PlaceHolder;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext != null)
{
_originalFormat = Format;
UpdateDate();
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == DateProperty.PropertyName || (propertyName == IsFocusedProperty.PropertyName && !IsFocused && (Date.ToString("d") == DateTime.Now.ToString("d"))))
{
AssignValue();
}
if (propertyName == NullableDateProperty.PropertyName && NullableDate.HasValue)
{
Date = NullableDate.Value;
if (Date.ToString(_originalFormat) == DateTime.Now.ToString(_originalFormat))
{
UpdateDate();
}
}
}
public void CleanDate()
{
NullableDate = null;
UpdateDate();
}
public void AssignValue()
{
NullableDate = Date;
UpdateDate();
}
}
}
DataPicker Custom renderer:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using ClearButtonDemo.Droid;
using ClearButtonDemo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
using System.ComponentModel;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(NullableDatePicker), typeof(NullableDatePickerRenderer))]
namespace ClearButtonDemo.Droid
{
public class NullableDatePickerRenderer : ViewRenderer<NullableDatePicker, EditText>
{
public NullableDatePickerRenderer(Context context) : base(context)
{
}
DatePickerDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<NullableDatePicker> e)
{
base.OnElementChanged(e);
this.SetNativeControl(new Android.Widget.EditText(Forms.Context));
if (Control == null || e.NewElement == null)
return;
this.Control.Click += OnPickerClick;
this.Control.Text = Element.Date.ToString(Element.Format);
this.Control.KeyListener = null;
this.Control.FocusChange += OnPickerFocusChange;
this.Control.Enabled = Element.IsEnabled;
Element.ClearRequested += Element_ClearRequested;
}
private void Element_ClearRequested(object sender, EventArgs e)
{
this.Element.CleanDate();
Control.Text = this.Element.Format;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Xamarin.Forms.DatePicker.DateProperty.PropertyName || e.PropertyName == Xamarin.Forms.DatePicker.FormatProperty.PropertyName)
SetDate(Element.Date);
}
void OnPickerFocusChange(object sender, Android.Views.View.FocusChangeEventArgs e)
{
if (e.HasFocus)
{
ShowDatePicker();
}
}
protected override void Dispose(bool disposing)
{
if (Control != null)
{
this.Control.Click -= OnPickerClick;
this.Control.FocusChange -= OnPickerFocusChange;
if (_dialog != null)
{
_dialog.Hide();
_dialog.Dispose();
_dialog = null;
}
}
base.Dispose(disposing);
}
void OnPickerClick(object sender, EventArgs e)
{
ShowDatePicker();
}
void SetDate(DateTime date)
{
//ToString(Element.Format)
this.Control.Text = date.ToShortDateString();
Element.Date = date;
}
private void ShowDatePicker()
{
CreateDatePickerDialog(this.Element.Date.Year, this.Element.Date.Month - 1, this.Element.Date.Day);
_dialog.Show();
}
void CreateDatePickerDialog(int year, int month, int day)
{
NullableDatePicker view = Element;
_dialog = new DatePickerDialog(Context, (o, e) =>
{
view.Date = e.Date;
((IElementController)view).SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control.ClearFocus();
_dialog = null;
}, year, month, day);
_dialog.SetButton("OK", (sender, e) =>
{
SetDate(_dialog.DatePicker.DateTime);
this.Element.Format = this.Element._originalFormat;
this.Element.AssignValue();
});
_dialog.SetButton2("cancel", (sender, e) =>
{
// this.Element.CleanDate();
// Control.Text = this.Element.Format;
_dialog.Dismiss();
});
}
}
}
When you use it, XAML:
<local:NullableDatePicker x:Name="NumdataPicker"/>
.cs
NumdataPicker.clear();
For iOS, you need to continue to use DatePickerRenderer, modify your code to:
public class NullableDatePickerRenderer : DatePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && this.Control != null)
{
this.AddClearButton();
var entry = (NullableDatePicker)this.Element;
if (!entry.NullableDate.HasValue)
{
this.Control.Text = entry.PlaceHolder;
}
if (DeviceInfo.Idiom == DeviceIdiom.Tablet)
{
this.Control.Font = UIFont.SystemFontOfSize(25);
}
}
}
Upvotes: 0
Reputation: 2216
Maybe like this .
public class MyDatePicker : DatePicker
{
private string _format = null;
public static readonly BindableProperty NullableDateProperty = BindableProperty.Create<MyDatePicker, DateTime?>(p => p.NullableDate, null);
public DateTime? NullableDate
{
get { return (DateTime?)GetValue(NullableDateProperty); }
set { SetValue(NullableDateProperty, value); UpdateDate(); }
}
private void UpdateDate()
{
if (NullableDate.HasValue) { if (null != _format) Format = _format; Date = NullableDate.Value; }
else { _format = Format; Format = "pick ..."; }
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
UpdateDate();
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "Date") NullableDate = Date;
}
}
In Xaml
<local:MyDatePicker x:Name="myDatePicker"></local:MyDatePicker>
In MainPage.cs
{
InitializeComponent();
myDatePicker.NullableDate = null;
}
Upvotes: 0