feat: add year button implementation.
This commit is contained in:
@@ -7,30 +7,6 @@
|
|||||||
x:Class="Ursa.Demo.Pages.DatePickerDemo">
|
x:Class="Ursa.Demo.Pages.DatePickerDemo">
|
||||||
<StackPanel Margin="20" HorizontalAlignment="Left">
|
<StackPanel Margin="20" HorizontalAlignment="Left">
|
||||||
<u:CalendarMonthView />
|
<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/>
|
<u:Calendar/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ public class Calendar: TemplatedControl
|
|||||||
public const string PART_MonthView = "PART_MonthView";
|
public const string PART_MonthView = "PART_MonthView";
|
||||||
public const string PART_YearView = "PART_YearView";
|
public const string PART_YearView = "PART_YearView";
|
||||||
|
|
||||||
private CalendarMonthView? _monthGrid;
|
private CalendarMonthView? _monthView;
|
||||||
|
private CalendarYearView? _yearView;
|
||||||
private DatePickerState _state = DatePickerState.None;
|
private DatePickerState _state = DatePickerState.None;
|
||||||
|
|
||||||
|
|
||||||
@@ -78,22 +79,23 @@ public class Calendar: TemplatedControl
|
|||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
if (_monthGrid is not null)
|
if (_monthView is not null)
|
||||||
{
|
{
|
||||||
_monthGrid.OnCalendarDayButtonPressed -= OnCalendarDayButtonPressed;
|
_monthView.OnDateSelected -= OnDateSelected;
|
||||||
_monthGrid.OnCalendarDayButtonPointerEnter -= OnCalendarDayButtonPointerEnter;
|
_monthView.OnDatePreviewed -= OnDatePreviewed;
|
||||||
}
|
}
|
||||||
_monthGrid = e.NameScope.Find<CalendarMonthView>(PART_MonthView);
|
_monthView = e.NameScope.Find<CalendarMonthView>(PART_MonthView);
|
||||||
if(_monthGrid is not null)
|
_yearView = e.NameScope.Find<CalendarYearView>(PART_YearView);
|
||||||
|
if(_monthView is not null)
|
||||||
{
|
{
|
||||||
_monthGrid.OnCalendarDayButtonPressed += OnCalendarDayButtonPressed;
|
_monthView.OnDateSelected += OnDateSelected;
|
||||||
_monthGrid.OnCalendarDayButtonPointerEnter += OnCalendarDayButtonPointerEnter;
|
_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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,38 +103,38 @@ public class Calendar: TemplatedControl
|
|||||||
if (_state is DatePickerState.None) return;
|
if (_state is DatePickerState.None) return;
|
||||||
if (_state == DatePickerState.PreviewStart)
|
if (_state == DatePickerState.PreviewStart)
|
||||||
{
|
{
|
||||||
_monthGrid.MarkPreview(date, EndDate);
|
_monthView.MarkPreview(date, EndDate);
|
||||||
}
|
}
|
||||||
else if (_state == DatePickerState.PreviewEnd)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
var date = e.Date;
|
var date = e.Date;
|
||||||
if (_state == DatePickerState.None)
|
if (_state == DatePickerState.None)
|
||||||
{
|
{
|
||||||
_monthGrid.ClearSelection();
|
_monthView.ClearSelection();
|
||||||
_monthGrid.ClearPreview();
|
_monthView.ClearPreview();
|
||||||
_monthGrid.MarkSelection(date, null);
|
_monthView.MarkSelection(date, null);
|
||||||
_state = DatePickerState.PreviewEnd;
|
_state = DatePickerState.PreviewEnd;
|
||||||
StartDate = date;
|
StartDate = date;
|
||||||
}
|
}
|
||||||
else if (_state == DatePickerState.PreviewStart)
|
else if (_state == DatePickerState.PreviewStart)
|
||||||
{
|
{
|
||||||
_monthGrid.MarkSelection(date, EndDate);
|
_monthView.MarkSelection(date, EndDate);
|
||||||
_state = DatePickerState.SelectStart;
|
_state = DatePickerState.SelectStart;
|
||||||
StartDate = date;
|
StartDate = date;
|
||||||
}
|
}
|
||||||
else if (_state == DatePickerState.PreviewEnd)
|
else if (_state == DatePickerState.PreviewEnd)
|
||||||
{
|
{
|
||||||
_monthGrid.MarkSelection(StartDate, date);
|
_monthView.MarkSelection(StartDate, date);
|
||||||
_state = DatePickerState.None;
|
_state = DatePickerState.None;
|
||||||
EndDate = date;
|
EndDate = date;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ using Avalonia.Controls.Metadata;
|
|||||||
using Avalonia.Controls.Mixins;
|
using Avalonia.Controls.Mixins;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Irihi.Avalonia.Shared.Common;
|
using Irihi.Avalonia.Shared.Common;
|
||||||
using Irihi.Avalonia.Shared.Helpers;
|
using Irihi.Avalonia.Shared.Helpers;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
@@ -128,6 +130,24 @@ public class CalendarDayButton: ContentControl
|
|||||||
PseudoClasses.Set(PC_NotCurrentMonth, value);
|
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()
|
static CalendarDayButton()
|
||||||
{
|
{
|
||||||
@@ -145,4 +165,22 @@ public class CalendarDayButton: ContentControl
|
|||||||
PseudoClasses.Set(PC_InRange, IsInRange);
|
PseudoClasses.Set(PC_InRange, IsInRange);
|
||||||
PseudoClasses.Set(PseudoClassName.PC_Selected, IsSelected);
|
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();
|
var cell = new CalendarDayButton();
|
||||||
cell.SetValue(Grid.RowProperty, i);
|
cell.SetValue(Grid.RowProperty, i);
|
||||||
cell.SetValue(Grid.ColumnProperty, j);
|
cell.SetValue(Grid.ColumnProperty, j);
|
||||||
cell.PointerPressed += OnDayButtonPressed;
|
cell.AddHandler(CalendarDayButton.DateSelectedEvent, OnCellDateSelected);
|
||||||
cell.PointerEntered += OnDayButtonPointerEnter;
|
cell.AddHandler(CalendarDayButton.DatePreviewedEvent, OnCellDatePreviewed);
|
||||||
children.Add(cell);
|
children.Add(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
_grid?.Children.AddRange(children);
|
_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)
|
private void SetDayButtons(DateTime date)
|
||||||
{
|
{
|
||||||
if (_grid is null) return;
|
if (_grid is null) return;
|
||||||
@@ -117,18 +129,6 @@ public class CalendarMonthView : TemplatedControl
|
|||||||
FadeOutDayButtons();
|
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)
|
private int PreviousMonthDays(DateTime date)
|
||||||
{
|
{
|
||||||
var firstDay = date.GetFirstDayOfMonth();
|
var firstDay = date.GetFirstDayOfMonth();
|
||||||
@@ -151,8 +151,8 @@ public class CalendarMonthView : TemplatedControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public event EventHandler<CalendarDayButtonEventArgs>? OnCalendarDayButtonPressed;
|
public event EventHandler<CalendarDayButtonEventArgs>? OnDateSelected;
|
||||||
public event EventHandler<CalendarDayButtonEventArgs>? OnCalendarDayButtonPointerEnter;
|
public event EventHandler<CalendarDayButtonEventArgs>? OnDatePreviewed;
|
||||||
|
|
||||||
public void MarkSelection(DateTime? start, DateTime? end)
|
public void MarkSelection(DateTime? start, DateTime? end)
|
||||||
{
|
{
|
||||||
@@ -164,12 +164,14 @@ public class CalendarMonthView : TemplatedControl
|
|||||||
if (d == start)
|
if (d == start)
|
||||||
{
|
{
|
||||||
button.IsStartDate = true;
|
button.IsStartDate = true;
|
||||||
|
button.IsPreviewStartDate = false;
|
||||||
button.IsEndDate = false;
|
button.IsEndDate = false;
|
||||||
button.IsInRange = false;
|
button.IsInRange = false;
|
||||||
}
|
}
|
||||||
else if (d == end)
|
else if (d == end)
|
||||||
{
|
{
|
||||||
button.IsEndDate = true;
|
button.IsEndDate = true;
|
||||||
|
button.IsPreviewEndDate = false;
|
||||||
button.IsStartDate = false;
|
button.IsStartDate = false;
|
||||||
button.IsInRange = 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;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -6,7 +11,82 @@ namespace Ursa.Controls;
|
|||||||
/// 2. show 12 years, one year per button (but only 10 buttons clickable)
|
/// 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)
|
/// 3. show 120 years, ten year per button (but only 10 buttons clickable)
|
||||||
/// </summary>
|
/// </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