feat: WIP.

This commit is contained in:
rabbitism
2024-06-19 22:16:28 +08:00
parent 0e73e8723d
commit b5e71f95c2
5 changed files with 284 additions and 222 deletions

View File

@@ -6,8 +6,6 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
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:CalendarYearView />
<u:CalendarView/> <u:CalendarView/>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@@ -4,7 +4,6 @@
xmlns:u="https://irihi.tech/ursa"> xmlns:u="https://irihi.tech/ursa">
<Design.PreviewWith> <Design.PreviewWith>
<StackPanel Margin="20" Spacing="5"> <StackPanel Margin="20" Spacing="5">
<u:CalendarMonthView />
<u:CalendarView /> <u:CalendarView />
</StackPanel> </StackPanel>
</Design.PreviewWith> </Design.PreviewWith>
@@ -119,144 +118,6 @@
<Setter Property="Cursor" Value="No" /> <Setter Property="Cursor" Value="No" />
</Style> </Style>
</ControlTheme> </ControlTheme>
<ControlTheme x:Key="{x:Type u:CalendarView}" TargetType="u:CalendarView">
<Setter Property="MinHeight" Value="300" />
<Setter Property="Template">
<ControlTemplate TargetType="u:CalendarView">
<Panel>
<Grid RowDefinitions="Auto, *">
<Grid Grid.Row="0" ColumnDefinitions="Auto, Auto,*, Auto, Auto">
<Button
Name="{x:Static u:CalendarView.PART_PreviousYearButton}"
Grid.Column="0"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemPreviousIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
<Button
Name="{x:Static u:CalendarView.PART_PreviousButton}"
Grid.Column="1"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemPreviousIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
<Grid Grid.Column="2" ColumnDefinitions="*, *">
<Button
Name="{x:Static u:CalendarView.PART_YearButton}"
Grid.Column="0"
HorizontalContentAlignment="Center"
Content="2024"
Foreground="{TemplateBinding Foreground}"
IsVisible="{TemplateBinding IsMonthMode}"
Theme="{DynamicResource BorderlessButton}" />
<Button
Name="{x:Static u:CalendarView.PART_MonthButton}"
Grid.Column="1"
HorizontalContentAlignment="Center"
Content="Apr"
Foreground="{TemplateBinding Foreground}"
IsVisible="{TemplateBinding IsMonthMode}"
Theme="{DynamicResource BorderlessButton}" />
<Button
Name="{x:Static u:CalendarView.PART_HeaderButton}"
Grid.Column="0"
Grid.ColumnSpan="2"
IsVisible="{TemplateBinding IsMonthMode, Converter={x:Static BoolConverters.Not}}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Content="2020-2030" />
</Grid>
<Button
Name="{x:Static u:CalendarView.PART_NextButton}"
Grid.Column="3"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemNextIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
<Button
Name="{x:Static u:CalendarView.PART_NextYearButton}"
Grid.Column="4"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemNextIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
</Grid>
<u:CalendarMonthView
Name="{x:Static u:CalendarView.PART_MonthView}"
Grid.Row="1"
VerticalAlignment="Top"
IsVisible="{TemplateBinding IsMonthMode}" />
<u:CalendarYearView
Name="{x:Static u:CalendarView.PART_YearView}"
Grid.Row="1"
Width="{Binding #PART_MonthView.Bounds.Width}"
Height="{Binding #PART_MonthView.Bounds.Height}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{TemplateBinding IsMonthMode,
Converter={x:Static BoolConverters.Not}}" />
</Grid>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:CalendarMonthView}" TargetType="u:CalendarMonthView">
<Setter Property="Template">
<ControlTemplate>
<Grid
Name="{x:Static u:CalendarMonthView.PART_Grid}"
ColumnDefinitions="*, *, *, *, *, *, *"
RowDefinitions="*, Auto, *, *, *, *, *, *">
<Rectangle
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="7"
Height="1"
Margin="8,8,8,0"
HorizontalAlignment="Stretch"
Fill="{DynamicResource SemiGrey2}" />
</Grid>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:CalendarYearView}" TargetType="u:CalendarYearView">
<Setter Property="Template">
<ControlTemplate>
<Grid
Name="{x:Static u:CalendarYearView.PART_Grid}"
ColumnDefinitions="*, *, *"
RowDefinitions="*, *, *, *" />
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:CalendarYearButton}" TargetType="u:CalendarYearButton"> <ControlTheme x:Key="{x:Type u:CalendarYearButton}" TargetType="u:CalendarYearButton">
<Setter Property="MinWidth" Value="32" /> <Setter Property="MinWidth" Value="32" />
<Setter Property="MinHeight" Value="32" /> <Setter Property="MinHeight" Value="32" />
@@ -298,4 +159,119 @@
</Style> </Style>
</Style> </Style>
</ControlTheme> </ControlTheme>
<ControlTheme x:Key="{x:Type u:CalendarView}" TargetType="u:CalendarView">
<Setter Property="MinHeight" Value="300" />
<Setter Property="Template">
<ControlTemplate TargetType="u:CalendarView">
<Panel>
<Grid RowDefinitions="Auto, *">
<Grid Grid.Row="0" ColumnDefinitions="Auto, Auto,*, Auto, Auto">
<Button
Name="{x:Static u:CalendarView.PART_PreviousYearButton}"
Grid.Column="0"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemPreviousIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
<Button
Name="{x:Static u:CalendarView.PART_PreviousButton}"
Grid.Column="1"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemPreviousIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
<Grid Grid.Column="2" ColumnDefinitions="*, *">
<Button
Name="{x:Static u:CalendarView.PART_YearButton}"
Grid.Column="0"
HorizontalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}" />
<Button
Name="{x:Static u:CalendarView.PART_MonthButton}"
Grid.Column="1"
HorizontalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}" />
<Button
Name="{x:Static u:CalendarView.PART_HeaderButton}"
Grid.Column="0"
IsVisible="True"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center" />
</Grid>
<Button
Name="{x:Static u:CalendarView.PART_NextButton}"
Grid.Column="3"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemNextIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
<Button
Name="{x:Static u:CalendarView.PART_NextYearButton}"
Grid.Column="4"
HorizontalContentAlignment="Left"
Foreground="{TemplateBinding Foreground}"
Theme="{DynamicResource BorderlessButton}">
<PathIcon
Width="12"
Height="12"
Data="{DynamicResource CalendarItemNextIconGlyph}"
Foreground="{DynamicResource CalendarItemIconForeground}" />
</Button>
</Grid>
<Grid
Name="{x:Static u:CalendarView.PART_MonthGrid}"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ColumnDefinitions="*, *, *, *, *, *, *"
RowDefinitions="*, Auto, *, *, *, *, *, *" />
<Grid
Name="{x:Static u:CalendarView.PART_YearGrid}"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ColumnDefinitions="*, *, *"
RowDefinitions="*, *, *, *" />
</Grid>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:month">
<Style Selector="^ /template/ Button#PART_YearButton, ^ /template/ Button#PART_MonthButton,^ /template/ Grid#PART_MonthGrid">
<Setter Property="Control.IsVisible" Value="True" />
</Style>
<Style Selector="^ /template/ Button#PART_HeaderButton, ^ /template/ Grid#PART_YearGrid">
<Setter Property="Control.IsVisible" Value="False" />
</Style>
</Style>
<Style Selector="^:not(:month)">
<Style Selector="^ /template/ Button#PART_YearButton, ^ /template/ Button#PART_MonthButton,^ /template/ Grid#PART_MonthGrid">
<Setter Property="Control.IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ Button#PART_HeaderButton, ^ /template/ Grid#PART_YearGrid">
<Setter Property="Control.IsVisible" Value="True" />
</Style>
</Style>
</ControlTheme>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -8,6 +8,7 @@ using Avalonia.Layout;
namespace Ursa.Controls; namespace Ursa.Controls;
/*
/// <summary> /// <summary>
/// Show days in a month. CalendarMonthView itself doesn't handle any date range selection logic. /// Show days in a month. CalendarMonthView itself doesn't handle any date range selection logic.
/// it provides a method to mark preview range and selection range. The range limit may out of current displayed month. /// it provides a method to mark preview range and selection range. The range limit may out of current displayed month.
@@ -227,3 +228,5 @@ public class CalendarMonthView : TemplatedControl
} }
} }
} }
*/

View File

@@ -1,14 +1,13 @@
using System.Globalization; using System.Diagnostics;
using System.Reflection; using System.Globalization;
using Avalonia; using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media.TextFormatting;
using Irihi.Avalonia.Shared.Helpers; using Irihi.Avalonia.Shared.Helpers;
using Calendar = System.Globalization.Calendar;
namespace Ursa.Controls; namespace Ursa.Controls;
@@ -19,11 +18,10 @@ namespace Ursa.Controls;
[TemplatePart(PART_YearButton, typeof(Button))] [TemplatePart(PART_YearButton, typeof(Button))]
[TemplatePart(PART_MonthButton, typeof(Button))] [TemplatePart(PART_MonthButton, typeof(Button))]
[TemplatePart(PART_HeaderButton, typeof(Button))] [TemplatePart(PART_HeaderButton, typeof(Button))]
[TemplatePart(PART_MonthView, typeof(CalendarMonthView))]
[TemplatePart(PART_YearView, typeof(CalendarYearView))]
[TemplatePart(PART_MonthGrid, typeof(Grid))] [TemplatePart(PART_MonthGrid, typeof(Grid))]
[TemplatePart(PART_YearGrid, typeof(Grid))] [TemplatePart(PART_YearGrid, typeof(Grid))]
public class CalendarView: TemplatedControl [PseudoClasses(PC_Month)]
public class CalendarView : TemplatedControl
{ {
public const string PART_NextYearButton = "PART_NextYearButton"; public const string PART_NextYearButton = "PART_NextYearButton";
public const string PART_PreviousYearButton = "PART_PreviousYearButton"; public const string PART_PreviousYearButton = "PART_PreviousYearButton";
@@ -31,51 +29,79 @@ public class CalendarView: TemplatedControl
public const string PART_PreviousButton = "PART_PreviousButton"; public const string PART_PreviousButton = "PART_PreviousButton";
public const string PART_YearButton = "PART_YearButton"; public const string PART_YearButton = "PART_YearButton";
public const string PART_MonthButton = "PART_MonthButton"; public const string PART_MonthButton = "PART_MonthButton";
public const string PART_MonthView = "PART_MonthView";
public const string PART_YearView = "PART_YearView";
public const string PART_HeaderButton = "PART_HeaderButton"; public const string PART_HeaderButton = "PART_HeaderButton";
public const string PART_MonthGrid = "PART_MonthGrid"; public const string PART_MonthGrid = "PART_MonthGrid";
public const string PART_YearGrid = "PART_YearGried"; public const string PART_YearGrid = "PART_YearGrid";
public const string PC_Month = ":month";
private const string ShortestDayName = "ShortestDayName"; private const string ShortestDayName = "ShortestDayName";
private readonly System.Globalization.Calendar _calendar = new GregorianCalendar(); internal static readonly DirectProperty<CalendarView, CalendarViewMode> ModeProperty =
AvaloniaProperty.RegisterDirect<CalendarView, CalendarViewMode>(
internal CalendarViewMode Mode; nameof(Mode), o => o.Mode, (o, v) => o.Mode = v, unsetValue: CalendarViewMode.Month);
//private CalendarMonthView? _monthView;
//private CalendarYearView? _yearView;
private Grid? _monthGrid;
private Grid? _yearGrid;
// Year button only shows the year in month mode.
private Button? _yearButton;
// Month button only shows the month in month mode.
private Button? _monthButton;
// Header button shows year in year mode, and year range in higher mode.
private Button? _headerButton;
public DateContext ContextDate { get; set; } = new DateContext();
public event EventHandler<CalendarDayButtonEventArgs>? OnDateSelected;
public event EventHandler<CalendarDayButtonEventArgs>? OnDatePreviewed;
public static readonly StyledProperty<bool> IsTodayHighlightedProperty = public static readonly StyledProperty<bool> IsTodayHighlightedProperty =
DatePickerBase.IsTodayHighlightedProperty.AddOwner<CalendarView>(); DatePickerBase.IsTodayHighlightedProperty.AddOwner<CalendarView>();
public static readonly StyledProperty<DayOfWeek> FirstDayOfWeekProperty =
DatePickerBase.FirstDayOfWeekProperty.AddOwner<CalendarView>();
private readonly Calendar _calendar = new GregorianCalendar();
// Header button shows year in year mode, and year range in higher mode.
private Button? _headerButton;
private CalendarViewMode _mode;
// Month button only shows the month in month mode.
private Button? _monthButton;
private Grid? _monthGrid;
// Year button only shows the year in month mode.
private Button? _yearButton;
private Grid? _yearGrid;
static CalendarView()
{
FirstDayOfWeekProperty.Changed.AddClassHandler<CalendarView, DayOfWeek>((view, args) =>
view.OnFirstDayOfWeekChanged(args));
ModeProperty.Changed.AddClassHandler<CalendarView, CalendarViewMode>((view, args) =>
{
view.PseudoClasses.Set(PC_Month, args.NewValue.Value == CalendarViewMode.Month);
Debug.WriteLine(args.NewValue.Value);
});
}
internal CalendarViewMode Mode
{
get => _mode;
set => SetAndRaise(ModeProperty, ref _mode, value);
}
public DateContext ContextDate { get; set; } = new();
public bool IsTodayHighlighted public bool IsTodayHighlighted
{ {
get => GetValue(IsTodayHighlightedProperty); get => GetValue(IsTodayHighlightedProperty);
set => SetValue(IsTodayHighlightedProperty, value); set => SetValue(IsTodayHighlightedProperty, value);
} }
public static readonly StyledProperty<DayOfWeek> FirstDayOfWeekProperty =
DatePickerBase.FirstDayOfWeekProperty.AddOwner<CalendarView>();
public DayOfWeek FirstDayOfWeek public DayOfWeek FirstDayOfWeek
{ {
get => GetValue(FirstDayOfWeekProperty); get => GetValue(FirstDayOfWeekProperty);
set => SetValue(FirstDayOfWeekProperty, value); set => SetValue(FirstDayOfWeekProperty, value);
} }
public event EventHandler<CalendarDayButtonEventArgs>? OnDateSelected;
public event EventHandler<CalendarDayButtonEventArgs>? OnDatePreviewed;
private void OnFirstDayOfWeekChanged(AvaloniaPropertyChangedEventArgs<DayOfWeek> args)
{
UpdateMonthViewHeader(args.NewValue.Value);
RefreshDayButtons();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
@@ -94,30 +120,40 @@ public class CalendarView: TemplatedControl
Button.ClickEvent.AddHandler(OnHeaderMonthButtonClick, _monthButton); Button.ClickEvent.AddHandler(OnHeaderMonthButtonClick, _monthButton);
Button.ClickEvent.AddHandler(OnHeaderButtonClick, _headerButton); Button.ClickEvent.AddHandler(OnHeaderButtonClick, _headerButton);
ContextDate = new DateContext(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day);
PseudoClasses.Set(PC_Month, Mode == CalendarViewMode.Month);
GenerateGridElements(); GenerateGridElements();
RefreshDayButtons();
RefreshYearButtons();
} }
/// <summary> /// <summary>
/// Rule: /// Rule:
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void OnHeaderButtonClick(object sender, RoutedEventArgs e) private void OnHeaderButtonClick(object sender, RoutedEventArgs e)
{ {
if (Mode == CalendarViewMode.Month) // Header button should be hidden in Month mode.
{ if (Mode == CalendarViewMode.Month) return;
throw new NotImplementedException();
}
if (Mode == CalendarViewMode.Year) if (Mode == CalendarViewMode.Year)
{ {
Mode = CalendarViewMode.Decade;
RefreshYearButtons();
return;
} }
if(Mode == CalendarViewMode.Decade)
{
Mode = CalendarViewMode.Century;
RefreshYearButtons();
return;
}
if (Mode == CalendarViewMode.Century) return;
} }
/// <summary> /// <summary>
/// Generate Buttons and labels for MonthView. /// Generate Buttons and labels for MonthView.
/// Generate Buttons for YearView. /// Generate Buttons for YearView.
/// </summary> /// </summary>
private void GenerateGridElements() private void GenerateGridElements()
{ {
@@ -147,6 +183,7 @@ public class CalendarView: TemplatedControl
cell.AddHandler(CalendarDayButton.DatePreviewedEvent, OnCellDatePreviewed); cell.AddHandler(CalendarDayButton.DatePreviewedEvent, OnCellDatePreviewed);
children.Add(cell); children.Add(cell);
} }
_monthGrid?.Children.AddRange(children); _monthGrid?.Children.AddRange(children);
// Generate month/year buttons. // Generate month/year buttons.
@@ -160,14 +197,16 @@ public class CalendarView: TemplatedControl
} }
} }
private void RefreshButtons(DateTime date) private void RefreshDayButtons()
{ {
if (_monthGrid is null) return; if (_monthGrid is null) return;
var children = _monthGrid.Children; var children = _monthGrid.Children;
var info = DateTimeHelper.GetCurrentDateTimeFormatInfo(); var info = DateTimeHelper.GetCurrentDateTimeFormatInfo();
var dayBefore = PreviousMonthDays(date); var date = new DateTime(ContextDate.Year.Value, ContextDate.Month.Value, ContextDate.Day.Value);
var dayBefore =
PreviousMonthDays(date);
var dateToSet = date.GetFirstDayOfMonth().AddDays(-dayBefore); var dateToSet = date.GetFirstDayOfMonth().AddDays(-dayBefore);
for (var i = 8; i < children.Count; i++) for (var i = 7; i < children.Count; i++)
{ {
var day = dateToSet; var day = dateToSet;
var cell = children[i] as CalendarDayButton; var cell = children[i] as CalendarDayButton;
@@ -181,15 +220,57 @@ public class CalendarView: TemplatedControl
FadeOutDayButtons(); FadeOutDayButtons();
} }
private void RefreshYearButtons()
{
if (_yearGrid is null) return;
var mode = Mode;
var contextDate = ContextDate;
for (var i = 0; i < 12; i++)
{
var child = _yearGrid.Children[i] as CalendarYearButton;
if (child is null) continue;
switch (mode)
{
case CalendarViewMode.Month:
child.SetValues(CalendarViewMode.Year, i);
break;
case CalendarViewMode.Year:
child.SetValues(CalendarViewMode.Decade, year: ContextDate.Year / 10 * 10 + i - 1);
break;
case CalendarViewMode.Decade:
var startYear = (ContextDate.Year / 10 + i - 1) * 10;
var endYear = (ContextDate.Year / 10 + i - 1) * 10 + 10;
child.SetValues(CalendarViewMode.Century, startYear: startYear, endYear: endYear);
break;
}
}
}
private void FadeOutDayButtons() private void FadeOutDayButtons()
{ {
if (_monthGrid is null) return; if (_monthGrid is null) return;
var children = _monthGrid.Children; var children = _monthGrid.Children;
for (var i = 8; i < children.Count; i++) for (var i = 7; i < children.Count; i++)
if (children[i] is CalendarDayButton { DataContext: DateTime d } button && d.Month != ContextDate.Month) if (children[i] is CalendarDayButton { DataContext: DateTime d } button && d.Month != ContextDate.Month)
button.IsNotCurrentMonth = true; button.IsNotCurrentMonth = true;
} }
private void UpdateMonthViewHeader(DayOfWeek day)
{
var dayOfWeek = (int)day;
var info = DateTimeHelper.GetCurrentDateTimeFormatInfo();
var texts = _monthGrid?.Children.Where(a => a is TextBlock { Tag: ShortestDayName }).ToList();
if (texts is not null)
for (var i = 0; i < 7; i++)
{
var d = (dayOfWeek + i) % DateTimeHelper.NumberOfDaysPerWeek;
texts[i].SetValue(TextBlock.TextProperty, info.ShortestDayNames[d]);
texts[i].SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Center);
texts[i].SetValue(Grid.RowProperty, 0);
texts[i].SetValue(Grid.ColumnProperty, i);
}
}
private int PreviousMonthDays(DateTime date) private int PreviousMonthDays(DateTime date)
{ {
var firstDay = date.GetFirstDayOfMonth(); var firstDay = date.GetFirstDayOfMonth();
@@ -210,66 +291,68 @@ public class CalendarView: TemplatedControl
} }
/// <summary> /// <summary>
/// Click on Month Header button. Calendar switch from month mode to year mode. /// Click on Month Header button. Calendar switch from month mode to year mode.
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void OnHeaderMonthButtonClick(object sender, RoutedEventArgs e) private void OnHeaderMonthButtonClick(object sender, RoutedEventArgs e)
{ {
_headerButton?.SetValue(ContentControl.ContentProperty, ContextDate.Year); _headerButton?.SetValue(ContentControl.ContentProperty, ContextDate.Year);
Mode = CalendarViewMode.Year; SetCurrentValue(ModeProperty, CalendarViewMode.Year);
IsVisibleProperty.SetValue(true, _yearGrid);
IsVisibleProperty.SetValue(false, _monthGrid);
} }
/// <summary> /// <summary>
/// Click on Year Header button. Calendar switch from month mode to decade mode. /// Click on Year Header button. Calendar switch from month mode to decade mode.
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void OnHeaderYearButtonClick(object sender, RoutedEventArgs e) private void OnHeaderYearButtonClick(object sender, RoutedEventArgs e)
{ {
if (_yearGrid is null) return; if (_yearGrid is null) return;
int? decadeStart = ContextDate.Year / 10 * 10; var decadeStart = ContextDate.Year / 10 * 10;
_headerButton?.SetValue(ContentControl.ContentProperty, _headerButton?.SetValue(ContentControl.ContentProperty, decadeStart + "-" + (decadeStart + 10));
decadeStart + "-" + (decadeStart + 10)); SetCurrentValue(ModeProperty, CalendarViewMode.Decade);
Mode = CalendarViewMode.Decade;
IsVisibleProperty.SetValue(true, _yearGrid);
IsVisibleProperty.SetValue(false, _monthGrid);
} }
/// <summary>
/// Click on CalendarYearButton in YearView.
/// Mode switch rules are:
/// 1. Month -> Not supported, buttons are hidden.
/// 2. Year -> Month: Set the date to the selected year and switch to Month mode.
/// 3. Decade -> Year: Set the date to the selected year and switch to Year mode.
/// 4. Century -> Decade: Set the date to the selected year and switch to Decade mode.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnYearItemSelected(object sender, CalendarYearButtonEventArgs e) private void OnYearItemSelected(object sender, CalendarYearButtonEventArgs e)
{ {
if (_yearGrid is null) return; if (_yearGrid is null) return;
var buttons = _yearGrid.Children.OfType<CalendarYearButton>().ToList(); var buttons = _yearGrid.Children.OfType<CalendarYearButton>().ToList();
if (e.Mode == CalendarViewMode.Year) if (Mode == CalendarViewMode.Year)
{ {
Mode = CalendarViewMode.Month;
if (e.Month is null) return; if (e.Month is null) return;
var day = MathHelpers.SafeClamp(e.Month.Value, 0, var day = MathHelpers.SafeClamp(e.Month.Value, 0,
DateTime.DaysInMonth(ContextDate.Year!.Value, e.Month.Value + 1)); DateTime.DaysInMonth(ContextDate.Year!.Value, e.Month.Value + 1));
ContextDate = new DateContext { Year = ContextDate.Year, Month = e.Month + 1, Day = day }; ContextDate = new DateContext { Year = ContextDate.Year, Month = e.Month + 1, Day = day };
Mode = CalendarViewMode.Month;
} }
else if (e.Mode == CalendarViewMode.Decade) else if (Mode == CalendarViewMode.Decade)
{ {
// Set CalendarYearView to Month mode Mode = CalendarViewMode.Year;
for (var i = 0; i < 12; i++) for (var i = 0; i < 12; i++) buttons[i].SetValues(CalendarViewMode.Year, i);
{ ContextDate = new DateContext { Year = e.Year!.Value, Month = null, Day = null };
buttons[i].SetValues(CalendarViewMode.Month, month: i);
}
ContextDate = new DateContext() { Year = e.Year!.Value, Month = ContextDate.Month, Day = null };
Mode = CalendarViewMode.Month;
} }
else if (e.Mode == CalendarViewMode.Century) else if (Mode == CalendarViewMode.Century)
{ {
// Set CalendarYearView to Year mode Mode = CalendarViewMode.Decade;
for (var i = 0; i < 12; i++) for (var i = 0; i < 12; i++)
{ {
if (e.StartYear is null || e.EndYear is null) continue; if (e.StartYear is null || e.EndYear is null) continue;
var year = e.StartYear.Value - 1 + i; var year = e.StartYear.Value - 1 + i;
buttons[i].SetValues(CalendarViewMode.Year, year: year); buttons[i].SetValues(CalendarViewMode.Decade, year: year);
} }
Mode = CalendarViewMode.Year;
} }
RefreshYearButtons();
} }
} }

View File

@@ -6,6 +6,7 @@ using Irihi.Avalonia.Shared.Helpers;
namespace Ursa.Controls; namespace Ursa.Controls;
/*
/// <summary> /// <summary>
/// Three modes: /// Three modes:
/// 1. show 12 months in a year /// 1. show 12 months in a year
@@ -117,3 +118,4 @@ public class CalendarYearView: TemplatedControl
RefreshButtons(); RefreshButtons();
} }
} }
*/