如何创建可为空的DatePickerControl?

问题描述 投票:0回答:4

我尝试在我的应用程序中使用 .NET MAUI 的日期选择器控件。我有一个要求,最初显示屏幕时需要在 StartDate 标签下方不显示任何内容或“-”。但日期选择器控件显示默认值,例如 01/01/1900。

现在的问题是如何修改日期选择器控件使其最初不显示任何默认值?

我使用网格内日期选择器上方的标签控件来完成此操作。但这是通过不同的控制来解决的。如何使用 DatePicker 控件本身来处理这个问题?可以吗?

更新于 2023 年 1 月 6 日

参考 XamGirl 的博客文章:https://xamgirl.com/clearable-datepicker-in-xamarin-forms/

正如 @Dongzhi Wang-MSFT 提到的,我们可以在 MAUI 中使用 Xamarin.Forms 渲染器。因此,我遵循相同的方法,在两个平台上创建了 NullDatePiceker 控件和自定义渲染器。但它只能在 Android 上运行。

iOS NullableDatePicker 渲染器

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 注册了一个渲染器。

有人对此有想法吗?

Xamarin.Forms 中的结果(在 MAUI 中显示 Null)

enter image description here

xamarin xamarin.forms xamarin.android maui
4个回答
0
投票

也许像这样。

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;
    }

0
投票

您可以使用自定义渲染器,就像这样

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);
            }
        }
    }

0
投票

我可以使用处理程序在 iOS 中通过清晰的按钮实现 Nullable Date。但现在完成按钮已停止工作。

  • 单击“完成”按钮时,它停止工作,现在单击“完成”按钮没有任何反应。 “完成”按钮应关闭选择器。

iOS 处理程序:

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);
        }
    }
}

0
投票

如果您正在寻找支持可为空的本机 DatePicker,请检查此包 https://www.nuget.org/packages/NPicker 它是基于.NET MAUI的源代码创建的,并修复了内置控件仍然存在的不可为空的问题。 完全支持 Windows、Android、iOS 和 MacOS

© www.soinside.com 2019 - 2024. All rights reserved.