Calendar page and shell improvements to support navigations. Enabled page caching.

This commit is contained in:
Burak Kaan Köse
2024-12-28 16:39:43 +01:00
parent fbc3ca4517
commit 6e3fcf363a
14 changed files with 227 additions and 153 deletions

View File

@@ -75,7 +75,7 @@ namespace Wino.Calendar
private void RegisterViewModels(IServiceCollection services)
{
services.AddSingleton(typeof(AppShellViewModel));
services.AddTransient(typeof(CalendarPageViewModel));
services.AddSingleton(typeof(CalendarPageViewModel));
services.AddTransient(typeof(CalendarSettingsPageViewModel));
services.AddTransient(typeof(AccountManagementViewModel));
}

View File

@@ -1,11 +1,10 @@
using System;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging;
using System.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Args;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Controls
{
@@ -21,6 +20,7 @@ namespace Wino.Calendar.Controls
public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null));
public static readonly DependencyProperty SelectedFlipViewIndexProperty = DependencyProperty.Register(nameof(SelectedFlipViewIndex), typeof(int), typeof(WinoCalendarControl), new PropertyMetadata(-1));
public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null));
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveCanvasChanged)));
public DayRangeRenderModel SelectedFlipViewDayRange
{
@@ -28,6 +28,12 @@ namespace Wino.Calendar.Controls
set { SetValue(SelectedFlipViewDayRangeProperty, value); }
}
public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
/// <summary>
/// Gets or sets the collection of day ranges to render.
/// Each day range usually represents a week, but it may support other ranges.
@@ -46,38 +52,6 @@ namespace Wino.Calendar.Controls
#endregion
private WinoDayTimelineCanvas _activeCanvas;
public WinoDayTimelineCanvas ActiveCanvas
{
get { return _activeCanvas; }
set
{
// FlipView's timeline is changing.
// Make sure to unregister from the old one.
if (_activeCanvas != null)
{
// Dismiss any selection on the old canvas.
_activeCanvas.SelectedDateTime = null;
_activeCanvas.TimelineCellSelected -= ActiveTimelineCellSelected;
_activeCanvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
}
_activeCanvas = value;
if (_activeCanvas != null)
{
_activeCanvas.TimelineCellSelected += ActiveTimelineCellSelected;
_activeCanvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
// Raise visible date range change to shell.
WeakReferenceMessenger.Default.Send(new VisibleDateRangeChangedMessage(_activeCanvas.RenderOptions.DateRange));
}
}
}
private WinoCalendarFlipView InternalFlipView;
public WinoCalendarControl()
@@ -86,6 +60,59 @@ namespace Wino.Calendar.Controls
SizeChanged += CalendarSizeChanged;
}
private static void OnActiveCanvasChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
{
if (e.OldValue is WinoDayTimelineCanvas oldCanvas)
{
// Dismiss any selection on the old canvas.
calendarControl.DeregisterCanvas(oldCanvas);
}
if (e.NewValue is WinoDayTimelineCanvas newCanvas)
{
calendarControl.RegisterCanvas(newCanvas);
}
calendarControl.ManageHighlightedDateRange();
}
}
private void ManageHighlightedDateRange()
{
if (ActiveCanvas == null)
{
SelectedFlipViewDayRange = null;
}
else
{
SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
}
}
private void DeregisterCanvas(WinoDayTimelineCanvas canvas)
{
if (canvas == null) return;
Debug.WriteLine("Deregister active canvas.");
canvas.SelectedDateTime = null;
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
}
private void RegisterCanvas(WinoDayTimelineCanvas canvas)
{
if (canvas == null) return;
Debug.WriteLine("Register new canvas.");
canvas.SelectedDateTime = null;
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
}
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActiveCanvas == null) return;
@@ -98,11 +125,6 @@ namespace Wino.Calendar.Controls
base.OnApplyTemplate();
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
// Each FlipViewItem will have 1 timeline canvas to draw hour cells in the background that supports selection of them.
// When the selection changes, we need to stop listening to the old canvas and start listening to the new one to catch events.
InternalFlipView.ActiveTimelineCanvasChanged += FlipViewsActiveTimelineCanvasChanged;
}
private void FlipViewsActiveTimelineCanvasChanged(object sender, WinoDayTimelineCanvas e)

View File

@@ -2,6 +2,7 @@
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.WinUI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Collections;
using Wino.Core.Domain.Models.Calendar;
@@ -10,29 +11,42 @@ namespace Wino.Calendar.Controls
{
public class WinoCalendarFlipView : CustomCalendarFlipView
{
public event EventHandler<WinoDayTimelineCanvas> ActiveTimelineCanvasChanged;
public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
public WinoCalendarFlipView()
{
SelectionChanged += CalendarDisplayRangeChanged;
RegisterPropertyChangedCallback(SelectedIndexProperty, new DependencyPropertyChangedCallback(OnSelectedIndexUpdated));
}
private async void CalendarDisplayRangeChanged(object sender, SelectionChangedEventArgs e)
private static void OnSelectedIndexUpdated(DependencyObject d, DependencyProperty e)
{
if (d is WinoCalendarFlipView flipView)
{
flipView.UpdateActiveCanvas();
}
}
public async void UpdateActiveCanvas()
{
if (SelectedIndex < 0)
ActiveTimelineCanvasChanged?.Invoke(this, null);
ActiveCanvas = null;
else
{
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
while (ContainerFromIndex(SelectedIndex) == null)
{
await Task.Delay(250);
await Task.Delay(100);
}
if (ContainerFromIndex(SelectedIndex) is FlipViewItem flipViewItem)
{
var canvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
ActiveTimelineCanvasChanged?.Invoke(this, canvas);
ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
}
}
}
@@ -43,42 +57,46 @@ namespace Wino.Calendar.Controls
/// <param name="dateTime">Date to navigate.</param>
public async void NavigateToDay(DateTime dateTime)
{
// Find the day range that contains the date.
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
await Task.Yield();
if (dayRange != null)
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
// Find the day range that contains the date.
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
if (dayRange != null)
{
// Difference between dates are high.
// No need to animate this much, just go without animating.
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
SelectedIndex = navigationItemIndex;
}
else
{
// Until we reach the day in the flip, simulate next-prev button clicks.
// This will make sure the FlipView animations are triggered.
// Setting SelectedIndex directly doesn't trigger the animations.
while (SelectedIndex != navigationItemIndex)
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
{
if (SelectedIndex > navigationItemIndex)
// Difference between dates are high.
// No need to animate this much, just go without animating.
SelectedIndex = navigationItemIndex;
}
else
{
// Until we reach the day in the flip, simulate next-prev button clicks.
// This will make sure the FlipView animations are triggered.
// Setting SelectedIndex directly doesn't trigger the animations.
while (SelectedIndex != navigationItemIndex)
{
GoPreviousFlip();
}
else
{
GoNextFlip();
if (SelectedIndex > navigationItemIndex)
{
GoPreviousFlip();
}
else
{
GoNextFlip();
}
}
}
}
}
});
}
public void NavigateHour(TimeSpan hourTimeSpan)
{
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.

View File

@@ -109,6 +109,7 @@
x:Name="PART_WinoFlipView"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ActiveCanvas="{x:Bind ActiveCanvas, Mode=TwoWay}"
IsTabStop="False"
ItemTemplate="{StaticResource FlipTemplate}"
ItemsSource="{TemplateBinding DayRanges}"

View File

@@ -169,6 +169,7 @@
<calendarControls:WinoCalendarView
x:Name="CalendarView"
HorizontalAlignment="Center"
DateClickedCommand="{x:Bind ViewModel.DateClickedCommand}"
HighlightedDateRange="{x:Bind ViewModel.HighlightedDateRange, Mode=OneWay}" />

View File

@@ -6,8 +6,7 @@ using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views
{
public sealed partial class AppShell : AppShellAbstract,
IRecipient<GoToCalendarDayMessage>
public sealed partial class AppShell : AppShellAbstract
{
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
private const string STATE_VerticalCalendar = "VerticalCalendar";
@@ -45,10 +44,10 @@ namespace Wino.Calendar.Views
}
public void Receive(GoToCalendarDayMessage message)
{
CalendarView.GoToDay(message.DateTime);
}
//public void Receive(GoToCalendarDayMessage message)
//{
// CalendarView.GoToDay(message.DateTime);
//}
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());

View File

@@ -2,8 +2,11 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Wino.Calendar.Args;
using Wino.Calendar.Views.Abstract;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views
@@ -17,6 +20,7 @@ namespace Wino.Calendar.Views
public CalendarPage()
{
InitializeComponent();
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
}
public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date);
@@ -25,6 +29,25 @@ namespace Wino.Calendar.Views
public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange();
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is CalendarPageNavigationArgs args)
{
if (args.RequestDefaultNavigation)
{
// Go today.
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(DateTime.Now.Date, CalendarInitInitiative.App));
}
else
{
// Go specified date.
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(args.NavigationDate, CalendarInitInitiative.User));
}
}
}
private void CellSelected(object sender, TimelineCellSelectedArgs e)
{
selectedDateTime = e.ClickedDate;