Merge pull request #15 from dotnet/datepicker

Implement basic DatePicker properties
This commit is contained in:
Javier Suárez 2021-03-09 10:35:55 +01:00 коммит произвёл GitHub
Родитель c1d32d1877 379eb614ca
Коммит 3d81b8fcdc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 362 добавлений и 15 удалений

Просмотреть файл

@ -93,15 +93,15 @@ Here you find a list of all controls with their (public) APIs and their status.
| ----|:-------:|:---:|:-----:|
| BackgroundColor | ✅ | ✅ | ✅ |
| CharacterSpacing | ⚠️ | ⚠️ | ⚠️ |
| Date | ⚠️ | ⚠️ | ⚠️ |
| Date | ✅ | ✅ | ✅ |
| DateSelected | ⚠️ | ⚠️ | ⚠️ |
| FontAttributes | ⚠️ | ⚠️ | ⚠️ |
| FontFamily | ⚠️ | ⚠️ | ⚠️ |
| FontSize | ⚠️ | ⚠️ | ⚠️ |
| Format | ⚠️ | ⚠️ | ⚠️ |
| MaximumDate | ⚠️ | ⚠️ | ⚠️ |
| MinimumDate | ⚠️ | ⚠️ | ⚠️ |
| TextColor | ⚠️ | ⚠️ | ⚠️ |
| MaximumDate | ✅ | ✅ | ✅ |
| MinimumDate | ✅ | ✅ | ✅ |
| TextColor | ✅ | ✅ | ✅ |
**Features**
| Feature | Description |

Просмотреть файл

@ -46,7 +46,10 @@
VisualType="Cupertino"
Text="CheckBox"/>
<graphics:DatePicker
VisualType="Cupertino"/>
VisualType="Cupertino"
MinimumDate="01/01/2021"
MaximumDate="12/31/2021"
Date="01/02/2021"/>
<graphics:Editor
VisualType="Cupertino"
Placeholder="Placeholder"/>
@ -90,7 +93,10 @@
IsChecked="True"
Text="CheckBox"/>
<graphics:DatePicker
VisualType="Fluent"/>
VisualType="Fluent"
MinimumDate="01/01/2021"
MaximumDate="12/31/2021"
Date="01/02/2021"/>
<graphics:Editor
VisualType="Fluent"
Placeholder="Placeholder"/>
@ -132,7 +138,10 @@
<graphics:CheckBox
VisualType="Material"/>
<graphics:DatePicker
VisualType="Material"/>
VisualType="Material"
MinimumDate="01/01/2021"
MaximumDate="12/31/2021"
Date="01/02/2021"/>
<graphics:Editor
VisualType="Material"
Placeholder="Placeholder"/>

Просмотреть файл

@ -47,7 +47,7 @@ namespace GraphicsControls
{
canvas.SaveState();
canvas.FontColor = ColorHelper.GetGraphicsColor(Material.Color.Black, Material.Color.White);
canvas.FontColor = TextColor.ToGraphicsColor(Material.Color.Black, Material.Color.White);
canvas.FontSize = 14f;
float margin = 8f;
@ -57,7 +57,7 @@ namespace GraphicsControls
var height = dirtyRect.Height;
var width = dirtyRect.Width;
canvas.DrawString(Date.ToShortDateString(), x, 0, width - margin, height, HorizontalAlignment.Left, VerticalAlignment.Center);
canvas.DrawString(GetDate().ToShortDateString(), x, 0, width - margin, height, HorizontalAlignment.Left, VerticalAlignment.Center);
canvas.RestoreState();
}

Просмотреть файл

@ -82,9 +82,9 @@ namespace GraphicsControls
canvas.SaveState();
if (IsEnabled)
canvas.FontColor = ColorHelper.GetGraphicsColor(Fluent.Color.Foreground.Black, Fluent.Color.Foreground.White);
canvas.FontColor = TextColor.ToGraphicsColor(Fluent.Color.Foreground.Black, Fluent.Color.Foreground.White);
else
canvas.FontColor = ColorHelper.GetGraphicsColor(Fluent.Color.Foreground.NeutralTertiary, Fluent.Color.Foreground.White);
canvas.FontColor = TextColor.ToGraphicsColor(Fluent.Color.Foreground.NeutralTertiary, Fluent.Color.Foreground.White);
canvas.FontSize = 14f;
@ -95,7 +95,7 @@ namespace GraphicsControls
var height = FluentDatePickerHeight;
var width = dirtyRect.Width;
canvas.DrawString(Date.ToShortDateString(), x, 0, width - margin, height, HorizontalAlignment.Left, VerticalAlignment.Center);
canvas.DrawString(GetDate().ToShortDateString(), x, 0, width - margin, height, HorizontalAlignment.Left, VerticalAlignment.Center);
canvas.RestoreState();
}

Просмотреть файл

@ -81,7 +81,7 @@ namespace GraphicsControls
{
canvas.SaveState();
canvas.FontColor = ColorHelper.GetGraphicsColor(Material.Color.Dark, Material.Color.Light);
canvas.FontColor = TextColor.ToGraphicsColor(Material.Color.Dark, Material.Color.Light);
canvas.FontSize = 16f;
float margin = 12f;
@ -99,7 +99,7 @@ namespace GraphicsControls
var height = dirtyRect.Height;
var width = dirtyRect.Width;
canvas.DrawString(Date.ToShortDateString(), x, 22f, width - margin, height, horizontalAlignment, VerticalAlignment.Top);
canvas.DrawString(GetDate().ToShortDateString(), x, 22f, width - margin, height, horizontalAlignment, VerticalAlignment.Top);
canvas.RestoreState();
}

Просмотреть файл

@ -1,12 +1,17 @@
using System;
using System.Collections.Generic;
using System.Graphics;
using System.Runtime.CompilerServices;
using GraphicsControls.Effects;
using Xamarin.Forms;
using XColor = Xamarin.Forms.Color;
namespace GraphicsControls
{
public partial class DatePicker : GraphicsVisualView
{
DatePickerDialogRoutingEffect _datePickerEffect;
public static class Layers
{
public const string Background = "DatePicker.Layers.Background";
@ -30,6 +35,9 @@ namespace GraphicsControls
BindableProperty.Create(nameof(MaximumDate), typeof(DateTime), typeof(DatePicker), new DateTime(2100, 12, 31),
validateValue: ValidateMaximumDate, coerceValue: CoerceMaximumDate);
public static readonly BindableProperty TextColorProperty =
BindableProperty.Create(nameof(TextColor), typeof(XColor), typeof(TimePicker), XColor.Default);
static object CoerceDate(BindableObject bindable, object value)
{
var picker = (DatePicker)bindable;
@ -98,6 +106,12 @@ namespace GraphicsControls
set { SetValue(MinimumDateProperty, value); }
}
public XColor TextColor
{
get { return (XColor)GetValue(TextColorProperty); }
set { SetValue(TextColorProperty, value); }
}
public List<string> DatePickerLayers = new List<string>
{
Layers.Background,
@ -126,6 +140,18 @@ namespace GraphicsControls
HeightRequest = 32;
break;
}
_datePickerEffect = new DatePickerDialogRoutingEffect();
Effects.Add(_datePickerEffect);
UpdateMaximumDate();
UpdateMinimumDate();
}
public override void Unload()
{
Effects.Remove(_datePickerEffect);
}
public override List<string> GraphicsLayers =>
@ -153,6 +179,16 @@ namespace GraphicsControls
}
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == MaximumDateProperty.PropertyName)
UpdateMaximumDate();
else if (propertyName == MinimumDateProperty.PropertyName)
UpdateMinimumDate();
}
protected virtual void DrawDatePickerBackground(ICanvas canvas, RectangleF dirtyRect)
{
switch (VisualType)
@ -223,5 +259,25 @@ namespace GraphicsControls
break;
}
}
void UpdateMaximumDate()
{
DatePickerDialog.SetMaximumDate(this, MaximumDate);
}
void UpdateMinimumDate()
{
DatePickerDialog.SetMinimumDate(this, MinimumDate);
}
DateTime GetDate()
{
var date = DatePickerDialog.GetDate(this);
if (date == default)
return MinimumDate;
return date;
}
}
}

Просмотреть файл

@ -0,0 +1,101 @@
using System;
using System.ComponentModel;
using Android.Views;
using GraphicsControls.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using static Android.Views.View;
using ADatePickerDialog = Android.App.DatePickerDialog;
using AView = Android.Views.View;
[assembly: ExportEffect(typeof(DatePickerDialogPlatformEffect), nameof(DatePickerDialog))]
namespace GraphicsControls.Effects
{
public class DatePickerDialogPlatformEffect : PlatformEffect
{
AView _view;
ADatePickerDialog _dialog;
protected override void OnAttached()
{
_view = Control ?? Container;
_view.Touch += OnTouch;
}
protected override void OnDetached()
{
var renderer = Container as IVisualElementRenderer;
if (_view != null)
_view.Touch -= OnTouch;
if (_dialog != null)
{
_dialog.Dispose();
_dialog = null;
}
_view = null;
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(e);
if (e.PropertyName == DatePickerDialog.MaximumDateProperty.PropertyName)
UpdateMaximumDate();
else if (e.PropertyName == DatePickerDialog.MinimumDateProperty.PropertyName)
UpdateMinimumDate();
}
void OnTouch(object sender, TouchEventArgs e)
{
if (e.Event.Action != MotionEventActions.Up)
return;
if (_dialog != null)
_dialog.Dispose();
CreateDialog();
UpdateMinimumDate();
UpdateMaximumDate();
_dialog.CancelEvent += OnCancelButtonClicked;
_dialog.Show();
}
void CreateDialog()
{
var date = DatePickerDialog.GetDate(Element);
_dialog = new ADatePickerDialog(_view.Context, (o, e) =>
{
DatePickerDialog.SetDate(Element, e.Date);
_view.ClearFocus();
_dialog.CancelEvent -= OnCancelButtonClicked;
_dialog = null;
}, date.Year, date.Month - 1, date.Day);
_dialog.SetCanceledOnTouchOutside(true);
}
void OnCancelButtonClicked(object sender, EventArgs e)
{
_view.ClearFocus();
}
void UpdateMaximumDate()
{
if (_dialog != null)
_dialog.DatePicker.MaxDate = (long)DatePickerDialog.GetMaximumDate(Element).ToUniversalTime().Subtract(DateTime.MinValue.AddYears(1969)).TotalMilliseconds;
}
void UpdateMinimumDate()
{
if (_dialog != null)
_dialog.DatePicker.MinDate = (long)DatePickerDialog.GetMinimumDate(Element).ToUniversalTime().Subtract(DateTime.MinValue.AddYears(1969)).TotalMilliseconds;
}
}
}

Просмотреть файл

@ -0,0 +1,125 @@
using System.ComponentModel;
using CoreGraphics;
using Foundation;
using GraphicsControls.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportEffect(typeof(DatePickerDialogPlatformEffect), nameof(DatePickerDialog))]
namespace GraphicsControls.Effects
{
public class DatePickerDialogPlatformEffect : PlatformEffect
{
UIView _view;
UIDatePicker _picker;
NoCaretField _entry;
NSDate _preSelectedDate;
protected override void OnAttached()
{
_view = Control ?? Container;
CreatePicker();
UpdateDate();
UpdateMaximumDate();
UpdateMinimumDate();
}
protected override void OnDetached()
{
_entry.RemoveFromSuperview();
_entry.Dispose();
_picker.Dispose();
_preSelectedDate.Dispose();
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(e);
if (e.PropertyName == DatePickerDialog.DateProperty.PropertyName)
UpdateDate();
else if (e.PropertyName == DatePickerDialog.MaximumDateProperty.PropertyName)
UpdateMaximumDate();
else if (e.PropertyName == DatePickerDialog.MinimumDateProperty.PropertyName)
UpdateMinimumDate();
}
void CreatePicker()
{
_entry = new NoCaretField
{
BorderStyle = UITextBorderStyle.None,
BackgroundColor = UIColor.Clear
};
_view.AddSubview(_entry);
_entry.TranslatesAutoresizingMaskIntoConstraints = false;
_entry.TopAnchor.ConstraintEqualTo(_view.TopAnchor).Active = true;
_entry.LeftAnchor.ConstraintEqualTo(_view.LeftAnchor).Active = true;
_entry.BottomAnchor.ConstraintEqualTo(_view.BottomAnchor).Active = true;
_entry.RightAnchor.ConstraintEqualTo(_view.RightAnchor).Active = true;
_entry.WidthAnchor.ConstraintEqualTo(_view.WidthAnchor).Active = true;
_entry.HeightAnchor.ConstraintEqualTo(_view.HeightAnchor).Active = true;
_view.UserInteractionEnabled = true;
_view.SendSubviewToBack(_entry);
_picker = new UIDatePicker { Mode = UIDatePickerMode.Date, TimeZone = new Foundation.NSTimeZone("UTC") };
if (UIDevice.CurrentDevice.CheckSystemVersion(14, 0))
{
_picker.PreferredDatePickerStyle = UIDatePickerStyle.Wheels;
}
var width = UIScreen.MainScreen.Bounds.Width;
var toolbar = new UIToolbar(new CGRect(0, 0, (float)width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true };
var cancelButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, (o, e) =>
{
_entry.ResignFirstResponder();
_picker.Date = _preSelectedDate;
});
var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) =>
{
_entry.ResignFirstResponder();
Done();
});
toolbar.SetItems(new[] { cancelButton, spacer, doneButton }, false);
_entry.InputView = _picker;
_entry.InputAccessoryView = toolbar;
}
void UpdateDate()
{
var date = DatePickerDialog.GetDate(Element).ToNSDate();
_picker.SetDate(date, false);
_preSelectedDate = date;
}
void UpdateMaximumDate()
{
_picker.MaximumDate = DatePickerDialog.GetMaximumDate(Element).ToNSDate();
}
void UpdateMinimumDate()
{
_picker.MinimumDate = DatePickerDialog.GetMinimumDate(Element).ToNSDate();
}
void Done()
{
var date = _picker.Date.ToDateTime().Date;
DatePickerDialog.SetDate(Element, date);
_preSelectedDate = _picker.Date;
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
using System;
using Xamarin.Forms;
namespace GraphicsControls.Effects
{
public class DatePickerDialog
{
public static readonly BindableProperty DateProperty =
BindableProperty.CreateAttached("Date", typeof(DateTime), typeof(DatePickerDialog), default(DateTime), defaultBindingMode: BindingMode.TwoWay);
public static void SetDate(BindableObject view, DateTime value)
{
view.SetValue(DateProperty, value);
}
public static DateTime GetDate(BindableObject view)
{
return (DateTime)view.GetValue(DateProperty);
}
public static readonly BindableProperty MaximumDateProperty =
BindableProperty.CreateAttached("MaximumDate", typeof(DateTime), typeof(DatePickerDialog), new DateTime(2100, 12, 31));
public static void SetMaximumDate(BindableObject view, DateTime value)
{
view.SetValue(MaximumDateProperty, value);
}
public static DateTime GetMaximumDate(BindableObject view)
{
return (DateTime)view.GetValue(MaximumDateProperty);
}
public static readonly BindableProperty MinimumDateProperty =
BindableProperty.CreateAttached("MinimumDate", typeof(DateTime), typeof(DatePickerDialog), new DateTime(1900, 1, 1));
public static void SetMinimumDate(BindableObject view, DateTime value)
{
view.SetValue(MinimumDateProperty, value);
}
public static DateTime GetMinimumDate(BindableObject view)
{
return (DateTime)view.GetValue(MinimumDateProperty);
}
}
internal class DatePickerDialogRoutingEffect : RoutingEffect
{
public DatePickerDialogRoutingEffect() : base("GraphicsControls." + nameof(DatePickerDialog))
{
}
}
}

Просмотреть файл

@ -8,7 +8,7 @@ using ATimePickerDialog = Android.App.TimePickerDialog;
using AView = Android.Views.View;
[assembly: ResolutionGroupName("GraphicsControls")]
[assembly: ExportEffect(typeof(TimePickerDialogPlatformEffect), nameof(GraphicsControls.Effects.TimePickerDialog))]
[assembly: ExportEffect(typeof(TimePickerDialogPlatformEffect), nameof(TimePickerDialog))]
namespace GraphicsControls.Effects
{
public class TimePickerDialogPlatformEffect : PlatformEffect