diff --git a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml
index 44f1296..a946536 100644
--- a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml
+++ b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml
@@ -7,30 +7,6 @@
x:Class="Ursa.Demo.Pages.DatePickerDemo">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Ursa/Controls/DateTimePicker/Calendar.cs b/src/Ursa/Controls/DateTimePicker/Calendar.cs
index 11a4f02..02288ba 100644
--- a/src/Ursa/Controls/DateTimePicker/Calendar.cs
+++ b/src/Ursa/Controls/DateTimePicker/Calendar.cs
@@ -26,7 +26,8 @@ public class Calendar: TemplatedControl
public const string PART_MonthView = "PART_MonthView";
public const string PART_YearView = "PART_YearView";
- private CalendarMonthView? _monthGrid;
+ private CalendarMonthView? _monthView;
+ private CalendarYearView? _yearView;
private DatePickerState _state = DatePickerState.None;
@@ -78,22 +79,23 @@ public class Calendar: TemplatedControl
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- if (_monthGrid is not null)
+ if (_monthView is not null)
{
- _monthGrid.OnCalendarDayButtonPressed -= OnCalendarDayButtonPressed;
- _monthGrid.OnCalendarDayButtonPointerEnter -= OnCalendarDayButtonPointerEnter;
+ _monthView.OnDateSelected -= OnDateSelected;
+ _monthView.OnDatePreviewed -= OnDatePreviewed;
}
- _monthGrid = e.NameScope.Find(PART_MonthView);
- if(_monthGrid is not null)
+ _monthView = e.NameScope.Find(PART_MonthView);
+ _yearView = e.NameScope.Find(PART_YearView);
+ if(_monthView is not null)
{
- _monthGrid.OnCalendarDayButtonPressed += OnCalendarDayButtonPressed;
- _monthGrid.OnCalendarDayButtonPointerEnter += OnCalendarDayButtonPointerEnter;
+ _monthView.OnDateSelected += OnDateSelected;
+ _monthView.OnDatePreviewed += OnDatePreviewed;
}
}
- private void OnCalendarDayButtonPointerEnter(object sender, CalendarDayButtonEventArgs e)
+ private void OnDatePreviewed(object sender, CalendarDayButtonEventArgs e)
{
- if(_monthGrid is null)
+ if(_monthView is null)
{
return;
}
@@ -101,38 +103,38 @@ public class Calendar: TemplatedControl
if (_state is DatePickerState.None) return;
if (_state == DatePickerState.PreviewStart)
{
- _monthGrid.MarkPreview(date, EndDate);
+ _monthView.MarkPreview(date, EndDate);
}
else if (_state == DatePickerState.PreviewEnd)
{
- _monthGrid.MarkPreview(StartDate, date);
+ _monthView.MarkPreview(StartDate, date);
}
}
- private void OnCalendarDayButtonPressed(object sender, CalendarDayButtonEventArgs e)
+ private void OnDateSelected(object sender, CalendarDayButtonEventArgs e)
{
- if(_monthGrid is null)
+ if(_monthView is null)
{
return;
}
var date = e.Date;
if (_state == DatePickerState.None)
{
- _monthGrid.ClearSelection();
- _monthGrid.ClearPreview();
- _monthGrid.MarkSelection(date, null);
+ _monthView.ClearSelection();
+ _monthView.ClearPreview();
+ _monthView.MarkSelection(date, null);
_state = DatePickerState.PreviewEnd;
StartDate = date;
}
else if (_state == DatePickerState.PreviewStart)
{
- _monthGrid.MarkSelection(date, EndDate);
+ _monthView.MarkSelection(date, EndDate);
_state = DatePickerState.SelectStart;
StartDate = date;
}
else if (_state == DatePickerState.PreviewEnd)
{
- _monthGrid.MarkSelection(StartDate, date);
+ _monthView.MarkSelection(StartDate, date);
_state = DatePickerState.None;
EndDate = date;
}
diff --git a/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs b/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs
index e2bf172..d15e7b0 100644
--- a/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs
+++ b/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs
@@ -4,8 +4,10 @@ using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
+using Avalonia.Interactivity;
using Irihi.Avalonia.Shared.Common;
using Irihi.Avalonia.Shared.Helpers;
+using Ursa.EventArgs;
namespace Ursa.Controls;
@@ -128,6 +130,24 @@ public class CalendarDayButton: ContentControl
PseudoClasses.Set(PC_NotCurrentMonth, value);
}
}
+
+ public static readonly RoutedEvent DateSelectedEvent = RoutedEvent.Register(
+ nameof(DateSelected), RoutingStrategies.Bubble);
+
+ public event EventHandler DateSelected
+ {
+ add => AddHandler(DateSelectedEvent, value);
+ remove => RemoveHandler(DateSelectedEvent, value);
+ }
+
+ public static readonly RoutedEvent DatePreviewedEvent = RoutedEvent.Register(
+ nameof(DatePreviewed), RoutingStrategies.Bubble);
+
+ public event EventHandler DatePreviewed
+ {
+ add => AddHandler(DateSelectedEvent, value);
+ remove => RemoveHandler(DateSelectedEvent, value);
+ }
static CalendarDayButton()
{
@@ -145,4 +165,22 @@ public class CalendarDayButton: ContentControl
PseudoClasses.Set(PC_InRange, IsInRange);
PseudoClasses.Set(PseudoClassName.PC_Selected, IsSelected);
}
+
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ base.OnPointerPressed(e);
+ if (this.DataContext is DateTime d)
+ {
+ RaiseEvent(new CalendarDayButtonEventArgs(d) { RoutedEvent = DateSelectedEvent, Source = this });
+ }
+ }
+
+ protected override void OnPointerEntered(PointerEventArgs e)
+ {
+ base.OnPointerEntered(e);
+ if (this.DataContext is DateTime d)
+ {
+ RaiseEvent(new CalendarDayButtonEventArgs(d) { RoutedEvent = DateSelectedEvent, Source = this });
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs b/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs
index 1e55785..2742cdd 100644
--- a/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs
+++ b/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs
@@ -88,14 +88,26 @@ public class CalendarMonthView : TemplatedControl
var cell = new CalendarDayButton();
cell.SetValue(Grid.RowProperty, i);
cell.SetValue(Grid.ColumnProperty, j);
- cell.PointerPressed += OnDayButtonPressed;
- cell.PointerEntered += OnDayButtonPointerEnter;
+ cell.AddHandler(CalendarDayButton.DateSelectedEvent, OnCellDateSelected);
+ cell.AddHandler(CalendarDayButton.DatePreviewedEvent, OnCellDatePreviewed);
children.Add(cell);
}
_grid?.Children.AddRange(children);
}
+ private void OnCellDatePreviewed(object sender, CalendarDayButtonEventArgs e)
+ {
+ OnDatePreviewed?.Invoke(sender, e);
+ }
+
+ private void OnCellDateSelected(object sender, CalendarDayButtonEventArgs e)
+ {
+ OnDateSelected?.Invoke(sender, e);
+ }
+
+
+
private void SetDayButtons(DateTime date)
{
if (_grid is null) return;
@@ -117,18 +129,6 @@ public class CalendarMonthView : TemplatedControl
FadeOutDayButtons();
}
- private void OnDayButtonPressed(object sender, PointerPressedEventArgs e)
- {
- if (sender is CalendarDayButton { DataContext: DateTime d })
- OnCalendarDayButtonPressed?.Invoke(this, new CalendarDayButtonEventArgs(d));
- }
-
- private void OnDayButtonPointerEnter(object sender, PointerEventArgs e)
- {
- if (sender is CalendarDayButton { DataContext: DateTime d })
- OnCalendarDayButtonPointerEnter?.Invoke(this, new CalendarDayButtonEventArgs(d));
- }
-
private int PreviousMonthDays(DateTime date)
{
var firstDay = date.GetFirstDayOfMonth();
@@ -151,8 +151,8 @@ public class CalendarMonthView : TemplatedControl
}
- public event EventHandler? OnCalendarDayButtonPressed;
- public event EventHandler? OnCalendarDayButtonPointerEnter;
+ public event EventHandler? OnDateSelected;
+ public event EventHandler? OnDatePreviewed;
public void MarkSelection(DateTime? start, DateTime? end)
{
@@ -164,12 +164,14 @@ public class CalendarMonthView : TemplatedControl
if (d == start)
{
button.IsStartDate = true;
+ button.IsPreviewStartDate = false;
button.IsEndDate = false;
button.IsInRange = false;
}
else if (d == end)
{
button.IsEndDate = true;
+ button.IsPreviewEndDate = false;
button.IsStartDate = false;
button.IsInRange = false;
}
diff --git a/src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs b/src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs
new file mode 100644
index 0000000..f46450a
--- /dev/null
+++ b/src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs
@@ -0,0 +1,46 @@
+using System.Diagnostics;
+using Avalonia.Controls;
+using Avalonia.Controls.Mixins;
+namespace Ursa.Controls;
+
+internal enum CalendarYearViewMode
+{
+ Month,
+ Year,
+ // The button represents ten year, with one year before and one year after, 12 in total.
+ YearRange,
+}
+public class CalendarYearButton: ContentControl
+{
+ static CalendarYearButton()
+ {
+ PressedMixin.Attach();
+ }
+
+ internal int Year { get; private set; }
+
+ internal int Month { get; private set; }
+
+ internal int StartYear { get; private set; }
+
+ internal int EndYear { get; private set; }
+
+ internal CalendarYearViewMode Mode { get; private set; }
+
+ internal void SetValues(CalendarYearViewMode mode, DateTime contextDate, int? month = null, int? year = null, int? startYear = null, int? endYear = null)
+ {
+ Debug.Assert(!(month is null && year is null && startYear is null && endYear is null));
+ Mode = mode;
+ Month = month ?? 0;
+ Year = year ?? 0;
+ StartYear = startYear ?? 0;
+ EndYear = endYear ?? 0;
+ Content = Mode switch
+ {
+ CalendarYearViewMode.Month => DateTimeHelper.GetCurrentDateTimeFormatInfo().AbbreviatedMonthNames[Month],
+ CalendarYearViewMode.Year => Year.ToString(),
+ CalendarYearViewMode.YearRange => StartYear + "-" + EndYear,
+ _ => Content
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs b/src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs
new file mode 100644
index 0000000..17294e6
--- /dev/null
+++ b/src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs
@@ -0,0 +1,8 @@
+using Avalonia.Interactivity;
+
+namespace Ursa.Controls;
+
+public class CalendarYearButtonEventArgs: RoutedEventArgs
+{
+
+}
\ No newline at end of file
diff --git a/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs b/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs
index 4cdc0cc..4be0249 100644
--- a/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs
+++ b/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs
@@ -1,3 +1,8 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Documents;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+
namespace Ursa.Controls;
///
@@ -6,7 +11,82 @@ namespace Ursa.Controls;
/// 2. show 12 years, one year per button (but only 10 buttons clickable)
/// 3. show 120 years, ten year per button (but only 10 buttons clickable)
///
-public class CalendarYearView
+[TemplatePart(PART_Grid, typeof(Grid))]
+public class CalendarYearView: TemplatedControl
{
+ public const string PART_Grid = "PART_Grid";
+
+ private CalendarYearViewMode _mode = CalendarYearViewMode.Month;
+
+ internal CalendarYearViewMode Mode
+ {
+ get => _mode;
+ set
+ {
+ _mode = value;
+ RefreshButtons();
+ }
+ }
+
+ private DateTime _contextDate = DateTime.Today;
+ internal DateTime ContextDate
+ {
+ get => _contextDate;
+ set
+ {
+ _contextDate = value;
+ RefreshButtons();
+ }
+ }
+
+ private Grid? _grid;
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _grid = e.NameScope.Find(PART_Grid);
+ GenerateGridElements();
+ }
+
+ public void GenerateGridElements()
+ {
+ if (_grid is null)
+ {
+ return;
+ }
+ _grid.Children.Clear();
+ for (var i = 0; i < 12; i++)
+ {
+ var button = new CalendarYearButton();
+ Grid.SetRow(button, i / 3);
+ Grid.SetColumn(button, i % 3);
+ _grid.Children.Add(button);
+ }
+ }
+
+ private void RefreshButtons()
+ {
+ if (_grid is null) return;
+ var mode = Mode;
+ var contextDate = ContextDate;
+ for (var i = 0; i < 12; i++)
+ {
+ var child = _grid.Children[i] as CalendarYearButton;
+ if (child is null) continue;
+ switch (mode)
+ {
+ case CalendarYearViewMode.Month:
+ child.SetValues(CalendarYearViewMode.Month, contextDate, month: i);
+ break;
+ case CalendarYearViewMode.Year:
+ child.SetValues(CalendarYearViewMode.Year, contextDate, year: ContextDate.Year + i);
+ break;
+ case CalendarYearViewMode.YearRange:
+ var startYear = ContextDate.Year - 1;
+ var endYear = ContextDate.Year + 10;
+ child.SetValues(CalendarYearViewMode.YearRange, contextDate, startYear: startYear, endYear: endYear);
+ break;
+ }
+ }
+ }
}
\ No newline at end of file