using System; using System.Collections.Specialized; using System.Linq; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Wino.Core.Domain.Collections; 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); private TextBlock HeaderDateDayText; private TextBlock ColumnHeaderText; private Border IsTodayBorder; private ItemsControl AllDayItemsControl; private CalendarEventCollection _boundEventsCollection; public CalendarDayModel DayModel { get { return (CalendarDayModel)GetValue(DayModelProperty); } set { SetValue(DayModelProperty, value); } } public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register( nameof(DayModel), typeof(CalendarDayModel), typeof(DayColumnControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged))); public DayColumnControl() { DefaultStyleKey = typeof(DayColumnControl); Unloaded += OnUnloaded; } 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; RegisterEventsCollectionHandlers(); UpdateValues(); } private static void OnRenderingPropertiesChanged(DependencyObject control, DependencyPropertyChangedEventArgs e) { if (control is DayColumnControl columnControl) { columnControl.RegisterEventsCollectionHandlers(); columnControl.UpdateValues(); } } 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; } private void EventsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { 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; } private void UpdateValues() { if (DayModel == null) return; if (HeaderDateDayText != null) { HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString(); } // Monthly template does not use it. if (ColumnHeaderText != null) { ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo); } UpdateEventItemsSource(); if (IsTodayBorder == null) return; bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date; VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false); UpdateLayout(); } }