我尝试在我的应用程序中使用 .NET MAUI 的日期选择器控件。我有一个要求,最初显示屏幕时需要在 StartDate 标签下方不显示任何内容或“-”。但日期选择器控件显示默认值,例如 01/01/1900。
现在的问题是如何修改日期选择器控件使其最初不显示任何默认值?
我使用网格内日期选择器上方的标签控件来完成此操作。但这是通过不同的控制来解决的。如何使用 DatePicker 控件本身来处理这个问题?可以吗?
参考 XamGirl 的博客文章:https://xamgirl.com/clearable-datepicker-in-xamarin-forms/
正如 @Dongzhi Wang-MSFT 提到的,我们可以在 MAUI 中使用 Xamarin.Forms 渲染器。因此,我遵循相同的方法,在两个平台上创建了 NullDatePiceker 控件和自定义渲染器。但它只能在 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();
}
}
}
}
这里当 OnElementChanged() 被调用时,this.Control 为 null。我已经使用 AddHandler 注册了一个渲染器。
有人对此有想法吗?
也许像这样。
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;
}
}
在 Xaml 中
<local:MyDatePicker x:Name="myDatePicker"></local:MyDatePicker>
在 MainPage.cs 中
{
InitializeComponent();
myDatePicker.NullableDate = null;
}
您可以使用自定义渲染器,就像这样
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 自定义渲染器:
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();
});
}
}
}
使用时, XAML:
<local:NullableDatePicker x:Name="NumdataPicker"/>
.cs
NumdataPicker.clear();
对于iOS,需要继续使用DatePickerRenderer,修改代码为:
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);
}
}
}
我可以使用处理程序在 iOS 中通过清晰的按钮实现 Nullable Date。但现在完成按钮已停止工作。
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);
}
}
}
如果您正在寻找支持可为空的本机 DatePicker,请检查此包 https://www.nuget.org/packages/NPicker 它是基于.NET MAUI的源代码创建的,并修复了内置控件仍然存在的不可为空的问题。 完全支持 Windows、Android、iOS 和 MacOS