2026-02-13 03:09:13 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
using System.Linq;
|
2025-12-26 20:46:48 +01:00
|
|
|
using Microsoft.UI.Xaml;
|
|
|
|
|
using Microsoft.UI.Xaml.Controls;
|
2026-02-13 03:09:13 +01:00
|
|
|
using Wino.Core.Domain.Collections;
|
2025-12-26 20:46:48 +01:00
|
|
|
using Wino.Core.Domain.Models.Calendar;
|
|
|
|
|
|
|
|
|
|
namespace Wino.Calendar.Controls;
|
|
|
|
|
|
|
|
|
|
public partial class DayColumnControl : Control
|
|
|
|
|
{
|
|
|
|
|
private const string PART_HeaderDateDayText = nameof(PART_HeaderDateDayText);
|
|
|
|
|
private const string PART_IsTodayBorder = nameof(PART_IsTodayBorder);
|
|
|
|
|
private const string PART_ColumnHeaderText = nameof(PART_ColumnHeaderText);
|
|
|
|
|
private const string PART_AllDayItemsControl = nameof(PART_AllDayItemsControl);
|
|
|
|
|
|
|
|
|
|
private const string TodayState = nameof(TodayState);
|
|
|
|
|
private const string NotTodayState = nameof(NotTodayState);
|
|
|
|
|
|
2026-02-27 20:12:43 +01:00
|
|
|
private TextBlock? HeaderDateDayText;
|
|
|
|
|
private TextBlock? ColumnHeaderText;
|
|
|
|
|
private Border? IsTodayBorder;
|
|
|
|
|
private ItemsControl? AllDayItemsControl;
|
|
|
|
|
private CalendarEventCollection? _boundEventsCollection;
|
2025-12-26 20:46:48 +01:00
|
|
|
|
|
|
|
|
public CalendarDayModel DayModel
|
|
|
|
|
{
|
|
|
|
|
get { return (CalendarDayModel)GetValue(DayModelProperty); }
|
|
|
|
|
set { SetValue(DayModelProperty, value); }
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 03:09:13 +01:00
|
|
|
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(
|
|
|
|
|
nameof(DayModel),
|
|
|
|
|
typeof(CalendarDayModel),
|
|
|
|
|
typeof(DayColumnControl),
|
|
|
|
|
new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
2025-12-26 20:46:48 +01:00
|
|
|
|
|
|
|
|
public DayColumnControl()
|
|
|
|
|
{
|
|
|
|
|
DefaultStyleKey = typeof(DayColumnControl);
|
2026-02-13 03:09:13 +01:00
|
|
|
Unloaded += OnUnloaded;
|
2025-12-26 20:46:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnApplyTemplate()
|
|
|
|
|
{
|
|
|
|
|
base.OnApplyTemplate();
|
|
|
|
|
|
|
|
|
|
HeaderDateDayText = GetTemplateChild(PART_HeaderDateDayText) as TextBlock;
|
|
|
|
|
ColumnHeaderText = GetTemplateChild(PART_ColumnHeaderText) as TextBlock;
|
|
|
|
|
IsTodayBorder = GetTemplateChild(PART_IsTodayBorder) as Border;
|
|
|
|
|
AllDayItemsControl = GetTemplateChild(PART_AllDayItemsControl) as ItemsControl;
|
|
|
|
|
|
2026-02-13 03:09:13 +01:00
|
|
|
RegisterEventsCollectionHandlers();
|
2025-12-26 20:46:48 +01:00
|
|
|
UpdateValues();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void OnRenderingPropertiesChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (control is DayColumnControl columnControl)
|
|
|
|
|
{
|
2026-02-13 03:09:13 +01:00
|
|
|
columnControl.RegisterEventsCollectionHandlers();
|
2025-12-26 20:46:48 +01:00
|
|
|
columnControl.UpdateValues();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 03:09:13 +01:00
|
|
|
private void OnUnloaded(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
DeregisterEventsCollectionHandlers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool IsMonthlyTemplate() => ColumnHeaderText == null;
|
|
|
|
|
|
|
|
|
|
private void RegisterEventsCollectionHandlers()
|
|
|
|
|
{
|
|
|
|
|
var nextCollection = DayModel?.EventsCollection;
|
|
|
|
|
if (ReferenceEquals(_boundEventsCollection, nextCollection))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DeregisterEventsCollectionHandlers();
|
|
|
|
|
|
|
|
|
|
_boundEventsCollection = nextCollection;
|
|
|
|
|
if (_boundEventsCollection == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
((INotifyCollectionChanged)_boundEventsCollection.AllDayEvents).CollectionChanged += EventsCollectionChanged;
|
|
|
|
|
((INotifyCollectionChanged)_boundEventsCollection.RegularEvents).CollectionChanged += EventsCollectionChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeregisterEventsCollectionHandlers()
|
|
|
|
|
{
|
|
|
|
|
if (_boundEventsCollection == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
((INotifyCollectionChanged)_boundEventsCollection.AllDayEvents).CollectionChanged -= EventsCollectionChanged;
|
|
|
|
|
((INotifyCollectionChanged)_boundEventsCollection.RegularEvents).CollectionChanged -= EventsCollectionChanged;
|
|
|
|
|
_boundEventsCollection = null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-27 20:12:43 +01:00
|
|
|
private void EventsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
2026-02-13 03:09:13 +01:00
|
|
|
{
|
|
|
|
|
UpdateEventItemsSource();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateEventItemsSource()
|
|
|
|
|
{
|
|
|
|
|
if (AllDayItemsControl == null || DayModel == null) return;
|
|
|
|
|
|
|
|
|
|
if (IsMonthlyTemplate())
|
|
|
|
|
{
|
|
|
|
|
// Month cells should show all events for the day, not only all-day/multi-day.
|
|
|
|
|
var monthlyItems = DayModel.EventsCollection.AllDayEvents
|
|
|
|
|
.Concat(DayModel.EventsCollection.RegularEvents)
|
|
|
|
|
.GroupBy(a => a.Id)
|
|
|
|
|
.Select(g => g.First())
|
|
|
|
|
.OrderBy(a => a.StartDate)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
AllDayItemsControl.ItemsSource = monthlyItems;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AllDayItemsControl.ItemsSource = DayModel.EventsCollection.AllDayEvents;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-26 20:46:48 +01:00
|
|
|
private void UpdateValues()
|
|
|
|
|
{
|
2026-02-13 03:09:13 +01:00
|
|
|
if (DayModel == null) return;
|
2025-12-26 20:46:48 +01:00
|
|
|
|
2026-02-13 03:09:13 +01:00
|
|
|
if (HeaderDateDayText != null)
|
|
|
|
|
{
|
|
|
|
|
HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString();
|
|
|
|
|
}
|
2025-12-26 20:46:48 +01:00
|
|
|
|
|
|
|
|
// Monthly template does not use it.
|
|
|
|
|
if (ColumnHeaderText != null)
|
|
|
|
|
{
|
|
|
|
|
ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 03:09:13 +01:00
|
|
|
UpdateEventItemsSource();
|
2025-12-26 20:46:48 +01:00
|
|
|
|
2026-02-13 03:09:13 +01:00
|
|
|
if (IsTodayBorder == null) return;
|
2025-12-26 20:46:48 +01:00
|
|
|
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;
|
|
|
|
|
|
|
|
|
|
VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false);
|
|
|
|
|
|
|
|
|
|
UpdateLayout();
|
|
|
|
|
}
|
|
|
|
|
}
|