diff --git a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml
index b3f4a56..44f1296 100644
--- a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml
+++ b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml
@@ -6,7 +6,7 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ursa.Demo.Pages.DatePickerDemo">
-
+
@@ -17,7 +17,7 @@
-
+
diff --git a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs
index fa6e70c..2c6057c 100644
--- a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs
+++ b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs
@@ -1,6 +1,8 @@
-using Avalonia;
+using System.Diagnostics;
+using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using Ursa.Controls;
namespace Ursa.Demo.Pages;
diff --git a/src/Ursa.Themes.Semi/Controls/Calendar.axaml b/src/Ursa.Themes.Semi/Controls/Calendar.axaml
index 409efa3..ca83de8 100644
--- a/src/Ursa.Themes.Semi/Controls/Calendar.axaml
+++ b/src/Ursa.Themes.Semi/Controls/Calendar.axaml
@@ -124,7 +124,7 @@
-
+
-
+
diff --git a/src/Ursa/Controls/DateTimePicker/Calendar.cs b/src/Ursa/Controls/DateTimePicker/Calendar.cs
index 70de6e7..e6d504a 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 Grid? _monthGrid;
+ private CalendarMonthView? _monthGrid;
+ private DatePickerState _state = DatePickerState.None;
public static readonly StyledProperty SelectedDateProperty = AvaloniaProperty.Register(nameof(SelectedDate), DateTime.Now);
@@ -71,17 +72,68 @@ public class Calendar: TemplatedControl
set => SetValue(BlackoutDateRuleProperty, value);
}
+ internal DateTime? StartDate;
+ internal DateTime? EndDate;
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- _monthGrid = e.NameScope.Find(PART_MonthView);
- }
-
- private void InitializeGrid()
- {
if (_monthGrid is not null)
{
-
+ _monthGrid.OnCalendarDayButtonPressed -= OnCalendarDayButtonPressed;
+ _monthGrid.OnCalendarDayButtonPointerEnter -= OnCalendarDayButtonPointerEnter;
+ }
+ _monthGrid = e.NameScope.Find(PART_MonthView);
+ if(_monthGrid is not null)
+ {
+ _monthGrid.OnCalendarDayButtonPressed += OnCalendarDayButtonPressed;
+ _monthGrid.OnCalendarDayButtonPointerEnter += OnCalendarDayButtonPointerEnter;
}
}
-}
\ No newline at end of file
+
+ private void OnCalendarDayButtonPointerEnter(object sender, CalendarDayButtonEventArgs e)
+ {
+ if(_monthGrid is null)
+ {
+ return;
+ }
+ var date = e.Date;
+ if (_state is DatePickerState.None) return;
+ if (_state == DatePickerState.PreviewStart)
+ {
+ _monthGrid.MarkPreview(date, EndDate);
+ }
+ else if (_state == DatePickerState.PreviewEnd)
+ {
+ _monthGrid.MarkPreview(StartDate, date);
+ }
+ }
+
+ private void OnCalendarDayButtonPressed(object sender, CalendarDayButtonEventArgs e)
+ {
+ if(_monthGrid is null)
+ {
+ return;
+ }
+ var date = e.Date;
+ if (_state == DatePickerState.None)
+ {
+ _monthGrid.ClearSelection();
+ _monthGrid.MarkSelection(date, null);
+ _state = DatePickerState.PreviewEnd;
+ StartDate = date;
+ }
+ else if (_state == DatePickerState.PreviewStart)
+ {
+ _monthGrid.MarkSelection(date, EndDate);
+ _state = DatePickerState.SelectStart;
+ StartDate = date;
+ }
+ else if (_state == DatePickerState.PreviewEnd)
+ {
+ _monthGrid.MarkSelection(StartDate, date);
+ _state = DatePickerState.None;
+ EndDate = date;
+ }
+ }
+}
diff --git a/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs b/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs
index aa9d07c..1e55785 100644
--- a/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs
+++ b/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs
@@ -4,23 +4,52 @@ using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
-using Avalonia.Interactivity;
using Avalonia.Layout;
-using Irihi.Avalonia.Shared;
namespace Ursa.Controls;
///
-/// Show days in a month.
+/// Show days in a month.
///
[TemplatePart(PART_Grid, typeof(Grid))]
-public class CalendarMonthView: TemplatedControl
+public class CalendarMonthView : TemplatedControl
{
public const string PART_Grid = "PART_Grid";
- internal Calendar? Owner { get; set; }
-
- private Grid? _grid;
+
+ public static readonly StyledProperty FirstDayOfWeekProperty =
+ AvaloniaProperty.Register(
+ nameof(FirstDayOfWeek));
+
private readonly System.Globalization.Calendar _calendar = new GregorianCalendar();
+
+ private DateTime _contextDate = DateTime.Today;
+
+ private Grid? _grid;
+
+ static CalendarMonthView()
+ {
+ FirstDayOfWeekProperty.Changed.AddClassHandler((view, args) =>
+ view.OnDayOfWeekChanged(args));
+ }
+
+ internal Calendar? Owner { get; set; }
+
+ ///
+ /// The DateTime used to generate the month view. This date will be within the month.
+ ///
+ public DateTime ContextDate
+ {
+ get => _contextDate;
+ set => _contextDate = value;
+ // GenerateGridElements();
+ }
+
+ public DayOfWeek FirstDayOfWeek
+ {
+ get => GetValue(FirstDayOfWeekProperty);
+ set => SetValue(FirstDayOfWeekProperty, value);
+ }
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
@@ -29,80 +58,50 @@ public class CalendarMonthView: TemplatedControl
SetDayButtons(DateTime.Today);
}
- private DateTime _contextDate = DateTime.Today;
- ///
- /// The DateTime used to generate the month view. This date will be within the month.
- ///
- public DateTime ContextDate
- {
- get => _contextDate;
- set
- {
- _contextDate = value;
- // GenerateGridElements();
- }
- }
-
- public static readonly StyledProperty FirstDayOfWeekProperty = AvaloniaProperty.Register(
- nameof(FirstDayOfWeek));
-
- public DayOfWeek FirstDayOfWeek
- {
- get => GetValue(FirstDayOfWeekProperty);
- set => SetValue(FirstDayOfWeekProperty, value);
- }
-
- static CalendarMonthView()
- {
- FirstDayOfWeekProperty.Changed.AddClassHandler((view, args) => view.OnDayOfWeekChanged(args));
- }
-
private void OnDayOfWeekChanged(AvaloniaPropertyChangedEventArgs args)
{
// throw new NotImplementedException();
}
-
+
private void GenerateGridElements()
{
// Generate Day titles (Sun, Mon, Tue, Wed, Thu, Fri, Sat) based on FirstDayOfWeek and culture.
- int count = 7 + 7 * 7;
+ var count = 7 + 7 * 7;
var children = new List(count);
- int dayOfWeek = (int)FirstDayOfWeek;
+ var dayOfWeek = (int)FirstDayOfWeek;
var info = DateTimeHelper.GetCurrentDateTimeFormatInfo();
- for (int i = 0; i < 7; i++)
+ for (var i = 0; i < 7; i++)
{
- int d = ((dayOfWeek + i) % DateTimeHelper.NumberOfDaysPerWeek);
- var cell = new TextBlock(){ Text = info.ShortestDayNames[d] };
+ var d = (dayOfWeek + i) % DateTimeHelper.NumberOfDaysPerWeek;
+ var cell = new TextBlock { Text = info.ShortestDayNames[d] };
cell.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Center);
cell.SetValue(Grid.RowProperty, 0);
cell.SetValue(Grid.ColumnProperty, i);
children.Add(cell);
}
-
+
// Generate day buttons.
- for (int i = 2; i < DateTimeHelper.NumberOfWeeksPerMonth+2; i++)
+ for (var i = 2; i < DateTimeHelper.NumberOfWeeksPerMonth + 2; i++)
+ for (var j = 0; j < DateTimeHelper.NumberOfDaysPerWeek; j++)
{
- for (int j = 0; j < DateTimeHelper.NumberOfDaysPerWeek; j++)
- {
- var cell = new CalendarDayButton();
- cell.SetValue(Grid.RowProperty, i);
- cell.SetValue(Grid.ColumnProperty, j);
- cell.PointerPressed += OnDayButtonPressed;
- cell.PointerEntered += OnDayButtonPointerEnter;
- children.Add(cell);
- }
+ var cell = new CalendarDayButton();
+ cell.SetValue(Grid.RowProperty, i);
+ cell.SetValue(Grid.ColumnProperty, j);
+ cell.PointerPressed += OnDayButtonPressed;
+ cell.PointerEntered += OnDayButtonPointerEnter;
+ children.Add(cell);
}
-
+
_grid?.Children.AddRange(children);
}
-
+
private void SetDayButtons(DateTime date)
{
if (_grid is null) return;
var children = _grid.Children;
var info = DateTimeHelper.GetCurrentDateTimeFormatInfo();
- int dayBefore = PreviousMonthDays(date);
+ var dayBefore = PreviousMonthDays(date);
var dateToSet = date.GetFirstDayOfMonth().AddDays(-dayBefore);
for (var i = 8; i < children.Count; i++)
{
@@ -114,75 +113,149 @@ public class CalendarMonthView: TemplatedControl
cell.Content = day.Day.ToString(info);
dateToSet = dateToSet.AddDays(1);
}
+
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})
- {
+ if (sender is CalendarDayButton { DataContext: DateTime d })
OnCalendarDayButtonPointerEnter?.Invoke(this, new CalendarDayButtonEventArgs(d));
- }
}
-
+
private int PreviousMonthDays(DateTime date)
{
var firstDay = date.GetFirstDayOfMonth();
var dayOfWeek = _calendar.GetDayOfWeek(firstDay);
- var firstDayOfWeek = this.FirstDayOfWeek;
- int i = (dayOfWeek - firstDayOfWeek + DateTimeHelper.NumberOfDaysPerWeek) % DateTimeHelper.NumberOfDaysPerWeek;
+ var firstDayOfWeek = FirstDayOfWeek;
+ var i = (dayOfWeek - firstDayOfWeek + DateTimeHelper.NumberOfDaysPerWeek) % DateTimeHelper.NumberOfDaysPerWeek;
return i == 0 ? DateTimeHelper.NumberOfDaysPerWeek : i;
}
-
+
///
- /// Make days out of current month fade out. These buttons are not disabled. They are just visually faded out.
+ /// Make days out of current month fade out. These buttons are not disabled. They are just visually faded out.
///
private void FadeOutDayButtons()
{
if (_grid is null) return;
var children = _grid.Children;
for (var i = 8; i < children.Count; i++)
- {
if (children[i] is CalendarDayButton { DataContext: DateTime d } button && d.Month != _contextDate.Month)
- {
button.IsNotCurrentMonth = true;
- }
- }
}
public event EventHandler? OnCalendarDayButtonPressed;
public event EventHandler? OnCalendarDayButtonPointerEnter;
- public void MarkSelection(DateTime start, DateTime end)
+ public void MarkSelection(DateTime? start, DateTime? end)
{
- if(_grid?.Children is null) return;
+ if (_grid?.Children is null) return;
foreach (var child in _grid.Children)
- {
if (child is CalendarDayButton { DataContext: DateTime d } button)
{
+ if (d.Month != _contextDate.Month) continue;
if (d == start)
{
button.IsStartDate = true;
+ button.IsEndDate = false;
+ button.IsInRange = false;
}
else if (d == end)
{
button.IsEndDate = true;
+ button.IsStartDate = false;
+ button.IsInRange = false;
}
else if (d > start && d < end)
{
button.IsInRange = true;
+ button.IsStartDate = false;
+ button.IsEndDate = false;
}
+ else
+ {
+ button.IsStartDate = false;
+ button.IsEndDate = false;
+ button.IsInRange = false;
+ }
+ }
+ }
+
+ public void MarkPreview(DateTime? start, DateTime? end)
+ {
+ if (_grid?.Children is null) return;
+ foreach (var child in _grid.Children)
+ {
+ if (child is not CalendarDayButton { DataContext: DateTime d } button) continue;
+ if (d == start)
+ {
+ button.IsPreviewStartDate = true;
+ button.IsPreviewEndDate = false;
+ button.IsInRange = false;
+ }
+ else if (d == end)
+ {
+ button.IsPreviewEndDate = true;
+ button.IsPreviewStartDate = false;
+ button.IsInRange = false;
+ }
+ else if (d > start && d < end)
+ {
+ button.IsInRange = true;
+ button.IsPreviewStartDate = false;
+ button.IsPreviewEndDate = false;
+ }
+ else
+ {
+ button.IsPreviewStartDate = false;
+ button.IsPreviewEndDate = false;
+ button.IsInRange = false;
}
}
}
+ public void ClearSelection()
+ {
+ if (_grid?.Children is null) return;
+ foreach (var child in _grid.Children)
+ {
+ if (child is not CalendarDayButton button) continue;
+ button.IsStartDate = false;
+ button.IsEndDate = false;
+ button.IsInRange = false;
+ }
+ }
+
+ public void ClearPreview()
+ {
+ if (_grid?.Children is null) return;
+ foreach (var child in _grid.Children)
+ {
+ if (child is not CalendarDayButton button) continue;
+ button.IsPreviewStartDate = false;
+ button.IsPreviewEndDate = false;
+ button.IsInRange = false;
+ }
+ }
+
+ public void MarkSelection(DateTime date)
+ {
+ if (_grid?.Children is null) return;
+ foreach (var child in _grid.Children)
+ {
+ if (child is not CalendarDayButton { DataContext: DateTime d } button) continue;
+ button.IsStartDate = false;
+ button.IsEndDate = false;
+ button.IsInRange = false;
+ if (d.Month != _contextDate.Month) continue;
+ button.IsSelected = d == date;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Ursa/Controls/DateTimePicker/DatePicker.cs b/src/Ursa/Controls/DateTimePicker/DatePicker.cs
new file mode 100644
index 0000000..23d2eec
--- /dev/null
+++ b/src/Ursa/Controls/DateTimePicker/DatePicker.cs
@@ -0,0 +1,8 @@
+using Avalonia.Controls.Primitives;
+
+namespace Ursa.Controls;
+
+public class DatePicker: TemplatedControl
+{
+
+}
\ No newline at end of file
diff --git a/src/Ursa/Controls/DateTimePicker/DatePickerState.cs b/src/Ursa/Controls/DateTimePicker/DatePickerState.cs
new file mode 100644
index 0000000..fec187f
--- /dev/null
+++ b/src/Ursa/Controls/DateTimePicker/DatePickerState.cs
@@ -0,0 +1,11 @@
+namespace Ursa.Controls;
+
+public enum DatePickerState
+{
+ None,
+ SelectSingle,
+ SelectStart,
+ SelectEnd,
+ PreviewStart,
+ PreviewEnd,
+}
\ No newline at end of file