using System; using System.Windows.Input; using CommunityToolkit.Diagnostics; using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; using Wino.Core.Domain.Models.Calendar; using Wino.Helpers; namespace Wino.Calendar.Controls; public partial class WinoCalendarView : Control { private const string PART_DayViewItemBorder = nameof(PART_DayViewItemBorder); private const string PART_CalendarView = nameof(PART_CalendarView); public static readonly DependencyProperty HighlightedDateRangeProperty = DependencyProperty.Register(nameof(HighlightedDateRange), typeof(DateRange), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnHighlightedDateRangeChanged))); public static readonly DependencyProperty VisibleDateBackgroundProperty = DependencyProperty.Register(nameof(VisibleDateBackground), typeof(Brush), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertiesChanged))); public static readonly DependencyProperty DateClickedCommandProperty = DependencyProperty.Register(nameof(DateClickedCommand), typeof(ICommand), typeof(WinoCalendarView), new PropertyMetadata(null)); public static readonly DependencyProperty TodayBackgroundColorProperty = DependencyProperty.Register(nameof(TodayBackgroundColor), typeof(Color), typeof(WinoCalendarView), new PropertyMetadata(null)); public Color TodayBackgroundColor { get { return (Color)GetValue(TodayBackgroundColorProperty); } set { SetValue(TodayBackgroundColorProperty, value); } } /// /// Gets or sets the command to execute when a date is picked. /// Unused. /// public ICommand DateClickedCommand { get { return (ICommand)GetValue(DateClickedCommandProperty); } set { SetValue(DateClickedCommandProperty, value); } } /// /// Gets or sets the highlighted range of dates. /// public DateRange HighlightedDateRange { get { return (DateRange)GetValue(HighlightedDateRangeProperty); } set { SetValue(HighlightedDateRangeProperty, value); } } public Brush VisibleDateBackground { get { return (Brush)GetValue(VisibleDateBackgroundProperty); } set { SetValue(VisibleDateBackgroundProperty, value); } } private CalendarView CalendarView; public WinoCalendarView() { DefaultStyleKey = typeof(WinoCalendarView); } protected override void OnApplyTemplate() { base.OnApplyTemplate(); CalendarView = GetTemplateChild(PART_CalendarView) as CalendarView; Guard.IsNotNull(CalendarView, nameof(CalendarView)); CalendarView.SelectedDatesChanged -= InternalCalendarViewSelectionChanged; CalendarView.SelectedDatesChanged += InternalCalendarViewSelectionChanged; // TODO: Should come from settings. CalendarView.FirstDayOfWeek = Windows.Globalization.DayOfWeek.Monday; // Everytime display mode changes, update the visible date range backgrounds. // If users go back from year -> month -> day, we need to update the visible date range backgrounds. CalendarView.RegisterPropertyChangedCallback(CalendarView.DisplayModeProperty, (s, e) => UpdateVisibleDateRangeBackgrounds()); } private void InternalCalendarViewSelectionChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args) { if (args.AddedDates?.Count > 0) { var clickedDate = args.AddedDates[0].Date; SetInnerDisplayDate(clickedDate); var clickArgs = new CalendarViewDayClickedEventArgs(clickedDate); DateClickedCommand?.Execute(clickArgs); } // Reset selection, we don't show selected dates but react to them. CalendarView.SelectedDates.Clear(); } private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is WinoCalendarView control) { control.UpdateVisibleDateRangeBackgrounds(); } } private void SetInnerDisplayDate(DateTime dateTime) => CalendarView?.SetDisplayDate(dateTime); // Changing selected dates will trigger the selection changed event. // It will behave like user clicked the date. public void GoToDay(DateTime dateTime) => CalendarView.SelectedDates.Add(dateTime); private static void OnHighlightedDateRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is WinoCalendarView control) { control.SetInnerDisplayDate(control.HighlightedDateRange.StartDate); control.UpdateVisibleDateRangeBackgrounds(); } } public void UpdateVisibleDateRangeBackgrounds() { if (HighlightedDateRange == null || VisibleDateBackground == null || TodayBackgroundColor == null || CalendarView == null) return; var markDateCalendarDayItems = WinoVisualTreeHelper.FindDescendants(CalendarView); foreach (var calendarDayItem in markDateCalendarDayItems) { var border = WinoVisualTreeHelper.GetChildObject(calendarDayItem, PART_DayViewItemBorder); if (border == null) return; if (calendarDayItem.Date.Date == DateTime.Today.Date) { border.Background = new SolidColorBrush(TodayBackgroundColor); } else if (calendarDayItem.Date.Date >= HighlightedDateRange.StartDate.Date && calendarDayItem.Date.Date < HighlightedDateRange.EndDate.Date) { border.Background = VisibleDateBackground; } else { border.Background = null; } } } }