diff --git a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml index 769dbc0dace..0a5910f1aec 100644 --- a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml +++ b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml @@ -49,6 +49,11 @@ + + + diff --git a/samples/ControlCatalog/Pages/CalendarPage.xaml b/samples/ControlCatalog/Pages/CalendarPage.xaml index 99727e91241..9da75587975 100644 --- a/samples/ControlCatalog/Pages/CalendarPage.xaml +++ b/samples/ControlCatalog/Pages/CalendarPage.xaml @@ -48,6 +48,12 @@ + + + + diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs index 7062a9af814..41b816cd573 100644 --- a/src/Avalonia.Controls/Calendar/Calendar.cs +++ b/src/Avalonia.Controls/Calendar/Calendar.cs @@ -1,4 +1,4 @@ -// (c) Copyright Microsoft Corporation. +// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Globalization; using Avalonia.Automation.Peers; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; @@ -353,6 +354,57 @@ public IBrush? HeaderBackground set => SetValue(HeaderBackgroundProperty, value); } + /// + /// Defines the property. + /// + public static readonly StyledProperty IsWeekNumberVisibleProperty = + AvaloniaProperty.Register(nameof(IsWeekNumberVisible)); + + /// + /// Gets or sets a value indicating whether week numbers are shown in the month view. + /// + public bool IsWeekNumberVisible + { + get => GetValue(IsWeekNumberVisibleProperty); + set => SetValue(IsWeekNumberVisibleProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty WeekNumberRuleProperty = + AvaloniaProperty.Register( + nameof(WeekNumberRule), + defaultValue: (CalendarWeekNumberRule)DateTimeHelper.GetCurrentDateFormat().CalendarWeekRule); + + /// + /// Gets or sets the rule used to determine the first week of the year for week number display. + /// The default is taken from the current culture. Use + /// for ISO 8601 week numbering. + /// + public CalendarWeekNumberRule WeekNumberRule + { + get => GetValue(WeekNumberRuleProperty); + set => SetValue(WeekNumberRuleProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty WeekNumberHeaderProperty = + AvaloniaProperty.Register(nameof(WeekNumberHeader), "#"); + + /// + /// Gets or sets the content displayed in the week-number column header cell. + /// Set this to a localized string such as "CW", "KW", or "Wk" + /// to give users context for the week-number column. Defaults to "#". + /// + public object? WeekNumberHeader + { + get => GetValue(WeekNumberHeaderProperty); + set => SetValue(WeekNumberHeaderProperty, value); + } + public static readonly StyledProperty DisplayModeProperty = AvaloniaProperty.Register( nameof(DisplayMode), @@ -2209,6 +2261,9 @@ static Calendar() DisplayDateProperty.Changed.AddClassHandler((x, e) => x.OnDisplayDateChanged(e)); DisplayDateStartProperty.Changed.AddClassHandler((x, e) => x.OnDisplayDateStartChanged(e)); DisplayDateEndProperty.Changed.AddClassHandler((x, e) => x.OnDisplayDateEndChanged(e)); + IsWeekNumberVisibleProperty.Changed.AddClassHandler((x, _) => x.UpdateMonths()); + WeekNumberRuleProperty.Changed.AddClassHandler((x, _) => x.UpdateMonths()); + WeekNumberHeaderProperty.Changed.AddClassHandler((x, _) => x.UpdateMonths()); KeyDownEvent.AddClassHandler((x, e) => x.Calendar_KeyDown(e)); KeyUpEvent.AddClassHandler((x, e) => x.Calendar_KeyUp(e)); } diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index e33298023f0..09cfac405bd 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -24,7 +24,7 @@ namespace Avalonia.Controls.Primitives [TemplatePart(PART_ElementNextButton, typeof(Button))] [TemplatePart(PART_ElementPreviousButton, typeof(Button))] [TemplatePart(PART_ElementYearView, typeof(Grid))] - [PseudoClasses(":calendardisabled")] + [PseudoClasses(":calendardisabled", ":hasweeknumbers")] public sealed class CalendarItem : TemplatedControl { /// @@ -49,6 +49,9 @@ public sealed class CalendarItem : TemplatedControl private readonly System.Globalization.Calendar _calendar = new GregorianCalendar(); + private CalendarWeekNumberLabel[] _weekNumberLabels = Array.Empty(); + private CalendarWeekNumberLabel? _weekNumberHeaderLabel; + internal Calendar? Owner { get; set; } internal CalendarDayButton? CurrentButton { get; set; } @@ -168,16 +171,18 @@ private void PopulateGrids() { if (MonthView != null) { - var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth; + // +7 for the week-number column (1 header + 6 data cells) + var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth + Calendar.RowsPerMonth; using var children = new PooledList(childCount); + // Day-of-week title cells — day columns are at 1-7 (shifted right by 1) for (int i = 0; i < Calendar.ColumnsPerMonth; i++) { if (DayTitleTemplate?.Build() is Control cell) { cell.DataContext = string.Empty; cell.SetValue(Grid.RowProperty, 0); - cell.SetValue(Grid.ColumnProperty, i); + cell.SetValue(Grid.ColumnProperty, i + 1); children.Add(cell); } } @@ -198,7 +203,7 @@ private void PopulateGrids() cell.Owner = Owner; } cell.SetValue(Grid.RowProperty, i); - cell.SetValue(Grid.ColumnProperty, j); + cell.SetValue(Grid.ColumnProperty, j + 1); cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown; cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp; cell.PointerEntered += cellMouseEntered; @@ -206,7 +211,26 @@ private void PopulateGrids() children.Add(cell); } } - + + // Week-number cells — added after existing cells to preserve child indices 0–48. + // Header placeholder (row 0, col 0) + _weekNumberHeaderLabel = new CalendarWeekNumberLabel(); + _weekNumberHeaderLabel.IsHeader = true; + _weekNumberHeaderLabel.SetValue(Grid.RowProperty, 0); + _weekNumberHeaderLabel.SetValue(Grid.ColumnProperty, 0); + children.Add(_weekNumberHeaderLabel); + + // Data labels (rows 1–6, col 0) + _weekNumberLabels = new CalendarWeekNumberLabel[Calendar.RowsPerMonth - 1]; + for (int i = 1; i < Calendar.RowsPerMonth; i++) + { + var label = new CalendarWeekNumberLabel(); + label.SetValue(Grid.RowProperty, i); + label.SetValue(Grid.ColumnProperty, 0); + _weekNumberLabels[i - 1] = label; + children.Add(label); + } + MonthView.Children.AddRange(children); } @@ -254,6 +278,10 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) NextButton = e.NameScope.Find