feat: add year button implementation.
This commit is contained in:
@@ -7,30 +7,6 @@
|
||||
x:Class="Ursa.Demo.Pages.DatePickerDemo">
|
||||
<StackPanel Margin="20" HorizontalAlignment="Left">
|
||||
<u:CalendarMonthView />
|
||||
<u:CalendarDayButton />
|
||||
<u:CalendarDayButton IsSelected="True" />
|
||||
<u:CalendarDayButton IsBlackout="True" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<u:CalendarDayButton IsStartDate="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsEndDate="True" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" DataValidationErrors.Errors="{Binding items}">
|
||||
<u:CalendarDayButton IsPreviewStartDate="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsPreviewEndDate="True" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsInRange="True" IsStartDate="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
<u:CalendarDayButton IsEndDate="True" IsInRange="True" />
|
||||
<u:CalendarDayButton IsInRange="True" />
|
||||
</StackPanel>
|
||||
<u:Calendar/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -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<CalendarMonthView>(PART_MonthView);
|
||||
if(_monthGrid is not null)
|
||||
_monthView = e.NameScope.Find<CalendarMonthView>(PART_MonthView);
|
||||
_yearView = e.NameScope.Find<CalendarYearView>(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;
|
||||
}
|
||||
|
||||
@@ -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<CalendarDayButtonEventArgs> DateSelectedEvent = RoutedEvent.Register<CalendarDayButton, CalendarDayButtonEventArgs>(
|
||||
nameof(DateSelected), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<CalendarDayButtonEventArgs> DateSelected
|
||||
{
|
||||
add => AddHandler(DateSelectedEvent, value);
|
||||
remove => RemoveHandler(DateSelectedEvent, value);
|
||||
}
|
||||
|
||||
public static readonly RoutedEvent<CalendarDayButtonEventArgs> DatePreviewedEvent = RoutedEvent.Register<CalendarDayButton, CalendarDayButtonEventArgs>(
|
||||
nameof(DatePreviewed), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<CalendarDayButtonEventArgs> 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<CalendarDayButtonEventArgs>? OnCalendarDayButtonPressed;
|
||||
public event EventHandler<CalendarDayButtonEventArgs>? OnCalendarDayButtonPointerEnter;
|
||||
public event EventHandler<CalendarDayButtonEventArgs>? OnDateSelected;
|
||||
public event EventHandler<CalendarDayButtonEventArgs>? 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;
|
||||
}
|
||||
|
||||
46
src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs
Normal file
46
src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs
Normal file
@@ -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<CalendarYearButton>();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class CalendarYearButtonEventArgs: RoutedEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
/// <summary>
|
||||
@@ -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)
|
||||
/// </summary>
|
||||
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<Grid>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user