Files
Wino-Mail/Wino.Mail.WinUI/Controls/Calendar/WinoCalendarControl.cs
T

295 lines
9.0 KiB
C#
Raw Normal View History

2025-12-26 20:46:48 +01:00
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
2025-12-27 19:16:24 +01:00
using CommunityToolkit.WinUI;
2025-12-26 20:46:48 +01:00
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Calendar.Args;
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Helpers;
namespace Wino.Calendar.Controls;
public partial class WinoCalendarControl : Control
{
private const string PART_WinoFlipView = nameof(PART_WinoFlipView);
private const string PART_IdleGrid = nameof(PART_IdleGrid);
public event EventHandler<TimelineCellSelectedArgs>? TimelineCellSelected;
public event EventHandler<TimelineCellUnselectedArgs>? TimelineCellUnselected;
2025-12-26 20:46:48 +01:00
public event EventHandler? ScrollPositionChanging;
2025-12-26 20:46:48 +01:00
#region Dependency Properties
/// <summary>
2025-12-31 13:28:53 +01:00
/// Gets or sets the collection of day ranges to render.
/// Each day range usually represents a week, but it may support other ranges.
2025-12-26 20:46:48 +01:00
/// </summary>
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty]
public partial ObservableCollection<DayRangeRenderModel>? DayRanges { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty(DefaultValue = -1)]
public partial int SelectedFlipViewIndex { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty]
public partial DayRangeRenderModel? SelectedFlipViewDayRange { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty]
public partial WinoDayTimelineCanvas? ActiveCanvas { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty(DefaultValue = true)]
public partial bool IsFlipIdle { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty]
public partial ScrollViewer? ActiveScrollViewer { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty]
public partial ItemsPanelTemplate? VerticalItemsPanelTemplate { get; set; }
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty]
public partial ItemsPanelTemplate? HorizontalItemsPanelTemplate { get; set; }
[GeneratedDependencyProperty(DefaultValue = CalendarOrientation.Horizontal)]
public partial CalendarOrientation Orientation { get; set; }
2025-12-26 20:46:48 +01:00
/// <summary>
2025-12-31 13:28:53 +01:00
/// Gets or sets the day-week-month-year display type.
/// Orientation is not determined by this property, but Orientation property.
/// This property is used to determine the template to use for the calendar.
2025-12-26 20:46:48 +01:00
/// </summary>
2025-12-31 13:28:53 +01:00
[GeneratedDependencyProperty(DefaultValue = CalendarDisplayType.Day)]
public partial CalendarDisplayType DisplayType { get; set; }
2025-12-26 20:46:48 +01:00
#endregion
private WinoCalendarFlipView? InternalFlipView;
private Grid? IdleGrid;
2025-12-26 20:46:48 +01:00
2025-12-31 13:28:53 +01:00
private ScrollViewer? _previousScrollViewer;
private WinoDayTimelineCanvas? _previousCanvas;
2025-12-26 20:46:48 +01:00
public WinoCalendarControl()
{
DefaultStyleKey = typeof(WinoCalendarControl);
SizeChanged += CalendarSizeChanged;
}
2025-12-31 13:28:53 +01:00
partial void OnVerticalItemsPanelTemplateChanged(ItemsPanelTemplate? newValue)
=> ManageCalendarOrientation();
partial void OnHorizontalItemsPanelTemplateChanged(ItemsPanelTemplate? newValue)
=> ManageCalendarOrientation();
partial void OnOrientationChanged(CalendarOrientation newValue)
=> ManageCalendarOrientation();
2026-02-13 03:09:13 +01:00
partial void OnDisplayTypeChanged(CalendarDisplayType newValue)
=> ManageDisplayType();
2025-12-31 13:28:53 +01:00
partial void OnIsFlipIdleChanged(bool newValue)
=> UpdateIdleState();
partial void OnActiveScrollViewerPropertyChanged(DependencyPropertyChangedEventArgs e)
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
var newValue = e.NewValue as ScrollViewer;
if (_previousScrollViewer != null)
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
DeregisterScrollChanges(_previousScrollViewer);
2025-12-26 20:46:48 +01:00
}
2025-12-31 13:28:53 +01:00
if (newValue != null)
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
RegisterScrollChanges(newValue);
2025-12-26 20:46:48 +01:00
}
2025-12-31 13:28:53 +01:00
_previousScrollViewer = newValue;
ManageHighlightedDateRange();
2025-12-26 20:46:48 +01:00
}
2025-12-31 13:28:53 +01:00
partial void OnActiveCanvasPropertyChanged(DependencyPropertyChangedEventArgs e)
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
var newValue = e.NewValue as WinoDayTimelineCanvas;
if (_previousCanvas != null)
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
DeregisterCanvas(_previousCanvas);
2025-12-26 20:46:48 +01:00
}
2025-12-31 13:28:53 +01:00
if (newValue != null)
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
RegisterCanvas(newValue);
2025-12-26 20:46:48 +01:00
}
2025-12-31 13:28:53 +01:00
_previousCanvas = newValue;
ManageHighlightedDateRange();
2025-12-26 20:46:48 +01:00
}
private void ManageCalendarOrientation()
{
if (InternalFlipView == null || HorizontalItemsPanelTemplate == null || VerticalItemsPanelTemplate == null) return;
InternalFlipView.ItemsPanel = Orientation == CalendarOrientation.Horizontal ? HorizontalItemsPanelTemplate : VerticalItemsPanelTemplate;
}
2026-02-13 03:09:13 +01:00
private void ManageDisplayType()
{
if (InternalFlipView == null) return;
InternalFlipView.DisplayType = DisplayType;
}
2025-12-26 20:46:48 +01:00
private void ManageHighlightedDateRange()
{
if (InternalFlipView?.IsProgrammaticNavigationInProgress == true)
{
return;
}
SelectedFlipViewDayRange = InternalFlipView?.SelectedItem as DayRangeRenderModel;
}
2025-12-26 20:46:48 +01:00
private void DeregisterCanvas(WinoDayTimelineCanvas canvas)
{
if (canvas == null) return;
canvas.SelectedDateTime = null;
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
}
private void RegisterCanvas(WinoDayTimelineCanvas canvas)
{
if (canvas == null) return;
canvas.SelectedDateTime = null;
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
}
private void RegisterScrollChanges(ScrollViewer scrollViewer)
{
if (scrollViewer == null) return;
scrollViewer.ViewChanging += ScrollViewChanging;
}
private void DeregisterScrollChanges(ScrollViewer scrollViewer)
{
if (scrollViewer == null) return;
scrollViewer.ViewChanging -= ScrollViewChanging;
}
private void ScrollViewChanging(object? sender, ScrollViewerViewChangingEventArgs e)
2025-12-26 20:46:48 +01:00
=> ScrollPositionChanging?.Invoke(this, EventArgs.Empty);
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActiveCanvas == null) return;
ActiveCanvas.SelectedDateTime = null;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (InternalFlipView != null)
{
InternalFlipView.ProgrammaticNavigationCompleted -= InternalFlipViewProgrammaticNavigationCompleted;
}
2025-12-26 20:46:48 +01:00
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
IdleGrid = GetTemplateChild(PART_IdleGrid) as Grid;
if (InternalFlipView != null)
{
InternalFlipView.ProgrammaticNavigationCompleted += InternalFlipViewProgrammaticNavigationCompleted;
}
2025-12-26 20:46:48 +01:00
UpdateIdleState();
ManageCalendarOrientation();
2026-02-13 03:09:13 +01:00
ManageDisplayType();
2025-12-26 20:46:48 +01:00
}
private void InternalFlipViewProgrammaticNavigationCompleted(object? sender, ProgrammaticNavigationCompletedEventArgs e)
{
SelectedFlipViewDayRange = e.DayRange;
}
2025-12-26 20:46:48 +01:00
private void UpdateIdleState()
{
2026-02-27 10:22:52 +01:00
if (InternalFlipView != null)
{
InternalFlipView.Opacity = IsFlipIdle ? 0 : 1;
}
if (IdleGrid != null)
{
IdleGrid.Visibility = IsFlipIdle ? Visibility.Visible : Visibility.Collapsed;
}
2025-12-26 20:46:48 +01:00
}
private void ActiveTimelineCellUnselected(object? sender, TimelineCellUnselectedArgs e)
2025-12-26 20:46:48 +01:00
=> TimelineCellUnselected?.Invoke(this, e);
private void ActiveTimelineCellSelected(object? sender, TimelineCellSelectedArgs e)
2025-12-26 20:46:48 +01:00
=> TimelineCellSelected?.Invoke(this, e);
2026-01-05 15:10:33 +01:00
public void NavigateToDay(DateTime dateTime) => InternalFlipView?.NavigateToDay(dateTime);
2025-12-26 20:46:48 +01:00
public async void NavigateToHour(TimeSpan timeSpan)
{
if (ActiveScrollViewer == null) return;
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.
await Task.Yield();
2025-12-27 19:16:24 +01:00
await DispatcherQueue.EnqueueAsync(() =>
2025-12-26 20:46:48 +01:00
{
2025-12-31 13:28:53 +01:00
if (ActiveScrollViewer == null) return;
2025-12-26 20:46:48 +01:00
double hourHeght = 60;
double totalHeight = ActiveScrollViewer.ScrollableHeight;
double scrollPosition = timeSpan.TotalHours * hourHeght;
ActiveScrollViewer.ChangeView(null, scrollPosition, null, disableAnimation: false);
});
}
public void ResetTimelineSelection()
{
if (ActiveCanvas == null) return;
ActiveCanvas.SelectedDateTime = null;
}
public void GoNextRange()
{
if (InternalFlipView == null) return;
InternalFlipView.GoNextFlip();
}
public void GoPreviousRange()
{
if (InternalFlipView == null) return;
InternalFlipView.GoPreviousFlip();
}
public void UnselectActiveTimelineCell()
{
if (ActiveCanvas == null) return;
ActiveCanvas.SelectedDateTime = null;
}
public CalendarItemControl GetCalendarItemControl(CalendarItemViewModel calendarItemViewModel)
{
return this.FindDescendants<CalendarItemControl>().FirstOrDefault(a => a.CalendarItem == calendarItemViewModel)!;
2025-12-26 20:46:48 +01:00
}
}