using Avalonia.Automation.Peers; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Controls.Shapes; using Avalonia.Data; using Avalonia.Interactivity; using Avalonia.Layout; using System; using System.Collections.Generic; using System.Globalization; namespace Avalonia.Controls { /// /// A control to allow the user to select a date /// [TemplatePart("PART_ButtonContentGrid", typeof(Grid))] [TemplatePart("PART_DayTextBlock", typeof(TextBlock))] [TemplatePart("PART_FirstSpacer", typeof(Rectangle))] [TemplatePart("PART_FlyoutButton", typeof(Button))] [TemplatePart("PART_MonthTextBlock", typeof(TextBlock))] [TemplatePart("PART_PickerPresenter", typeof(DatePickerPresenter))] [TemplatePart("PART_Popup", typeof(Popup))] [TemplatePart("PART_SecondSpacer", typeof(Rectangle))] [TemplatePart("PART_YearTextBlock", typeof(TextBlock))] [PseudoClasses(":hasnodate")] public class DatePicker : TemplatedControl { /// /// Define the Property /// public static readonly StyledProperty DayFormatProperty = AvaloniaProperty.Register(nameof(DayFormat), "%d"); /// /// Defines the Property /// public static readonly StyledProperty DayVisibleProperty = AvaloniaProperty.Register(nameof(DayVisible), true); /// /// Defines the Property /// public static readonly StyledProperty MaxYearProperty = AvaloniaProperty.Register(nameof(MaxYear), DateTimeOffset.MaxValue, coerce: CoerceMaxYear); /// /// Defines the Property /// public static readonly StyledProperty MinYearProperty = AvaloniaProperty.Register(nameof(MinYear), DateTimeOffset.MinValue, coerce: CoerceMinYear); /// /// Defines the Property /// public static readonly StyledProperty MonthFormatProperty = AvaloniaProperty.Register(nameof(MonthFormat), "MMMM"); /// /// Defines the Property /// public static readonly StyledProperty MonthVisibleProperty = AvaloniaProperty.Register(nameof(MonthVisible), true); /// /// Defines the Property /// public static readonly StyledProperty YearFormatProperty = AvaloniaProperty.Register(nameof(YearFormat), "yyyy"); /// /// Defines the Property /// public static readonly StyledProperty YearVisibleProperty = AvaloniaProperty.Register(nameof(YearVisible), true); /// /// Defines the Property /// public static readonly StyledProperty SelectedDateProperty = AvaloniaProperty.Register(nameof(SelectedDate), defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); // Template Items private Button? _flyoutButton; private TextBlock? _dayText; private TextBlock? _monthText; private TextBlock? _yearText; private Grid? _container; private Rectangle? _spacer1; private Rectangle? _spacer2; private Popup? _popup; private DatePickerPresenter? _presenter; private bool _areControlsAvailable; public DatePicker() { PseudoClasses.Set(":hasnodate", true); var now = DateTimeOffset.Now; SetCurrentValue(MinYearProperty, new DateTimeOffset(now.Date.Year - 100, 1, 1, 0, 0, 0, now.Offset)); SetCurrentValue(MaxYearProperty, new DateTimeOffset(now.Date.Year + 100, 12, 31, 0, 0, 0, now.Offset)); } private static void OnGridVisibilityChanged(DatePicker sender, AvaloniaPropertyChangedEventArgs e) => sender.SetGrid(); public string DayFormat { get => GetValue(DayFormatProperty); set => SetValue(DayFormatProperty, value); } /// /// Gets or sets whether the day is visible /// public bool DayVisible { get => GetValue(DayVisibleProperty); set => SetValue(DayVisibleProperty, value); } /// /// Gets or sets the maximum year for the picker /// public DateTimeOffset MaxYear { get => GetValue(MaxYearProperty); set => SetValue(MaxYearProperty, value); } private static DateTimeOffset CoerceMaxYear(AvaloniaObject sender, DateTimeOffset value) { if (value < sender.GetValue(MinYearProperty)) { throw new InvalidOperationException($"{MaxYearProperty.Name} cannot be less than {MinYearProperty.Name}"); } return value; } private void OnMaxYearChanged(DateTimeOffset? value) { if (SelectedDate.HasValue && SelectedDate.Value > value) SetCurrentValue(SelectedDateProperty, value); } /// /// Gets or sets the minimum year for the picker /// public DateTimeOffset MinYear { get => GetValue(MinYearProperty); set => SetValue(MinYearProperty, value); } private static DateTimeOffset CoerceMinYear(AvaloniaObject sender, DateTimeOffset value) { if (value > sender.GetValue(MaxYearProperty)) { throw new InvalidOperationException($"{MinYearProperty.Name} cannot be greater than {MaxYearProperty.Name}"); } return value; } private void OnMinYearChanged(DateTimeOffset? value) { if (SelectedDate.HasValue && SelectedDate.Value < value) SetCurrentValue(SelectedDateProperty, value); } /// /// Gets or sets the month format /// public string MonthFormat { get => GetValue(MonthFormatProperty); set => SetValue(MonthFormatProperty, value); } /// /// Gets or sets whether the month is visible /// public bool MonthVisible { get => GetValue(MonthVisibleProperty); set => SetValue(MonthVisibleProperty, value); } /// /// Gets or sets the year format /// public string YearFormat { get => GetValue(YearFormatProperty); set => SetValue(YearFormatProperty, value); } /// /// Gets or sets whether the year is visible /// public bool YearVisible { get => GetValue(YearVisibleProperty); set => SetValue(YearVisibleProperty, value); } /// /// Gets or sets the Selected Date for the picker, can be null /// public DateTimeOffset? SelectedDate { get => GetValue(SelectedDateProperty); set => SetValue(SelectedDateProperty, value); } /// /// Raised when the changes /// public event EventHandler? SelectedDateChanged; protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { _areControlsAvailable = false; if (_flyoutButton != null) _flyoutButton.Click -= OnFlyoutButtonClicked; if (_presenter != null) { _presenter.Confirmed -= OnConfirmed; _presenter.Dismissed -= OnDismissPicker; } base.OnApplyTemplate(e); _flyoutButton = e.NameScope.Find