37
Wino.Calendar.ViewModels/AccountManagementViewModel.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Store;
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
|
||||
{
|
||||
public AccountManagementViewModel(ICalendarDialogService dialogService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
INavigationService navigationService,
|
||||
IAccountService accountService,
|
||||
IProviderService providerService,
|
||||
IStoreManagementService storeManagementService,
|
||||
IAuthenticationProvider authenticationProvider,
|
||||
IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
|
||||
{
|
||||
CalendarDialogService = dialogService;
|
||||
}
|
||||
|
||||
public ICalendarDialogService CalendarDialogService { get; }
|
||||
|
||||
public override Task InitializeAccountsAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
var t = await StoreManagementService.HasProductAsync(StoreProductType.UnlimitedAccounts);
|
||||
}
|
||||
}
|
||||
}
|
||||
237
Wino.Calendar.ViewModels/AppShellViewModel.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Extensions;
|
||||
using Wino.Core.MenuItems;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
public partial class AppShellViewModel : CalendarBaseViewModel,
|
||||
IRecipient<VisibleDateRangeChangedMessage>,
|
||||
IRecipient<CalendarEnableStatusChangedMessage>,
|
||||
IRecipient<CalendarInitializedMessage>,
|
||||
IRecipient<NavigateManageAccountsRequested>
|
||||
{
|
||||
public event EventHandler<CalendarDisplayType> DisplayTypeChanged;
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
public IStatePersistanceService StatePersistenceService { get; }
|
||||
public INavigationService NavigationService { get; }
|
||||
public IWinoServerConnectionManager ServerConnectionManager { get; }
|
||||
public MenuItemCollection FooterItems { get; set; }
|
||||
public MenuItemCollection MenuItems { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
private IMenuItem _selectedMenuItem;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isCalendarEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the active connection status of the Wino server.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private WinoServerConnectionStatus activeConnectionStatus;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display date of the calendar.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DateTimeOffset _displayDate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the highlighted range in the CalendarView and displayed date range in FlipView.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DateRange highlightedDateRange;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableRangeCollection<string> dateNavigationHeaderItems = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedDateNavigationHeaderIndex;
|
||||
|
||||
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
|
||||
|
||||
public AppShellViewModel(IPreferencesService preferencesService,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
INavigationService navigationService,
|
||||
IWinoServerConnectionManager serverConnectionManager)
|
||||
{
|
||||
NavigationService = navigationService;
|
||||
ServerConnectionManager = serverConnectionManager;
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
StatePersistenceService = statePersistanceService;
|
||||
StatePersistenceService.StatePropertyChanged += PrefefencesChanged;
|
||||
}
|
||||
|
||||
private void PrefefencesChanged(object sender, string e)
|
||||
{
|
||||
if (e == nameof(StatePersistenceService.CalendarDisplayType))
|
||||
{
|
||||
DisplayTypeChanged?.Invoke(this, StatePersistenceService.CalendarDisplayType);
|
||||
OnPropertyChanged(nameof(IsVerticalCalendar));
|
||||
|
||||
// Change the calendar.
|
||||
DateClicked(new CalendarViewDayClickedEventArgs(GetDisplayTypeSwitchDate()));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
CreateFooterItems();
|
||||
UpdateDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
partial void OnSelectedMenuItemChanged(IMenuItem oldValue, IMenuItem newValue)
|
||||
{
|
||||
if (newValue is SettingsItem)
|
||||
{
|
||||
NavigationService.Navigate(WinoPage.SettingsPage);
|
||||
}
|
||||
else if (newValue is ManageAccountsMenuItem)
|
||||
{
|
||||
NavigationService.Navigate(WinoPage.AccountManagementPage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When calendar type switches, we need to navigate to the most ideal date.
|
||||
/// This method returns that date.
|
||||
/// </summary>
|
||||
private DateTime GetDisplayTypeSwitchDate()
|
||||
{
|
||||
switch (StatePersistenceService.CalendarDisplayType)
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
if (HighlightedDateRange.IsInRange(DateTime.Now)) return DateTime.Now.Date;
|
||||
|
||||
return HighlightedDateRange.StartDate;
|
||||
case CalendarDisplayType.Week:
|
||||
// TODO: From settings
|
||||
if (HighlightedDateRange.IsInRange(DateTime.Now)) return DateTime.Now.Date.GetWeekStartDateForDate(DayOfWeek.Monday);
|
||||
|
||||
return HighlightedDateRange.StartDate.GetWeekStartDateForDate(DayOfWeek.Monday);
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
break;
|
||||
case CalendarDisplayType.Month:
|
||||
break;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DateTime.Today.Date;
|
||||
}
|
||||
|
||||
protected override void OnDispatcherAssigned()
|
||||
{
|
||||
base.OnDispatcherAssigned();
|
||||
|
||||
MenuItems = new MenuItemCollection(Dispatcher);
|
||||
FooterItems = new MenuItemCollection(Dispatcher);
|
||||
}
|
||||
|
||||
public override void OnPageLoaded()
|
||||
{
|
||||
base.OnPageLoaded();
|
||||
|
||||
NavigationService.Navigate(WinoPage.CalendarPage, new CalendarPageNavigationArgs()
|
||||
{
|
||||
RequestDefaultNavigation = true
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateFooterItems()
|
||||
{
|
||||
FooterItems.Clear();
|
||||
FooterItems.Add(new ManageAccountsMenuItem());
|
||||
FooterItems.Add(new SettingsItem());
|
||||
}
|
||||
|
||||
#region Commands
|
||||
|
||||
[RelayCommand]
|
||||
private void TodayClicked() => Messenger.Send(new GoToCalendarDayMessage(DateTime.Now.Date));
|
||||
|
||||
[RelayCommand]
|
||||
public void ManageAccounts() => NavigationService.Navigate(WinoPage.AccountManagementPage);
|
||||
|
||||
[RelayCommand]
|
||||
private Task ReconnectServerAsync() => ServerConnectionManager.ConnectAsync();
|
||||
|
||||
[RelayCommand]
|
||||
private void DateClicked(CalendarViewDayClickedEventArgs clickedDate)
|
||||
=> Messenger.Send(new CalendarInitializeMessage(clickedDate.ClickedDate, CalendarInitInitiative.User));
|
||||
|
||||
#endregion
|
||||
|
||||
public void Receive(VisibleDateRangeChangedMessage message) => HighlightedDateRange = message.DateRange;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the header navigation items based on visible date range and calendar type.
|
||||
/// </summary>
|
||||
private void UpdateDateNavigationHeaderItems()
|
||||
{
|
||||
DateNavigationHeaderItems.Clear();
|
||||
|
||||
// TODO: From settings
|
||||
var testInfo = new CultureInfo("en-US");
|
||||
|
||||
switch (StatePersistenceService.CalendarDisplayType)
|
||||
{
|
||||
case CalendarDisplayType.Day:
|
||||
case CalendarDisplayType.Week:
|
||||
case CalendarDisplayType.WorkWeek:
|
||||
case CalendarDisplayType.Month:
|
||||
DateNavigationHeaderItems.ReplaceRange(testInfo.DateTimeFormat.MonthNames);
|
||||
break;
|
||||
case CalendarDisplayType.Year:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SetDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
partial void OnHighlightedDateRangeChanged(DateRange value) => SetDateNavigationHeaderItems();
|
||||
|
||||
private void SetDateNavigationHeaderItems()
|
||||
{
|
||||
if (HighlightedDateRange == null) return;
|
||||
|
||||
if (DateNavigationHeaderItems.Count == 0)
|
||||
{
|
||||
UpdateDateNavigationHeaderItems();
|
||||
}
|
||||
|
||||
// TODO: Year view
|
||||
var monthIndex = HighlightedDateRange.GetMostVisibleMonthIndex();
|
||||
|
||||
SelectedDateNavigationHeaderIndex = Math.Max(monthIndex - 1, -1);
|
||||
}
|
||||
|
||||
public async void Receive(CalendarEnableStatusChangedMessage message)
|
||||
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
|
||||
|
||||
// Calendar page is loaded and calendar is ready to recieve render requests.
|
||||
public void Receive(CalendarInitializedMessage message) => Messenger.Send(new GoToCalendarDayMessage(DateTime.Now.Date));
|
||||
|
||||
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItem = FooterItems.FirstOrDefault(a => a is ManageAccountsMenuItem);
|
||||
}
|
||||
}
|
||||
436
Wino.Calendar.ViewModels/CalendarPageViewModel.cs
Normal file
@@ -0,0 +1,436 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Calendar.Models.CalendarTypeStrategies;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.MenuItems;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
||||
IRecipient<CalendarInitializeMessage>
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ObservableRangeCollection<DayRangeRenderModel> _dayRanges = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedDateRangeIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private DayRangeRenderModel _selectedDayRange;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isCalendarEnabled = true;
|
||||
|
||||
// Get rid of some of the items if we have too many.
|
||||
private const int maxDayRangeSize = 10;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
|
||||
// Store latest rendered options.
|
||||
private CalendarDisplayType _currentDisplayType;
|
||||
private int _displayDayCount;
|
||||
|
||||
private SemaphoreSlim _calendarLoadingSemaphore = new(1);
|
||||
private bool isLoadMoreBlocked = false;
|
||||
private CalendarSettings _currentSettings = null;
|
||||
|
||||
public IStatePersistanceService StatePersistanceService { get; }
|
||||
|
||||
public CalendarPageViewModel(IStatePersistanceService statePersistanceService,
|
||||
IPreferencesService preferencesService)
|
||||
{
|
||||
StatePersistanceService = statePersistanceService;
|
||||
_preferencesService = preferencesService;
|
||||
|
||||
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
Messenger.Send(new CalendarInitializedMessage());
|
||||
}
|
||||
|
||||
// TODO: Replace when calendar settings are updated.
|
||||
// Should be a field ideally.
|
||||
private BaseCalendarTypeDrawingStrategy GetDrawingStrategy(CalendarDisplayType displayType)
|
||||
{
|
||||
return displayType switch
|
||||
{
|
||||
CalendarDisplayType.Day => new DayCalendarDrawingStrategy(_currentSettings),
|
||||
CalendarDisplayType.Week => new WeekCalendarDrawingStrategy(_currentSettings),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
partial void OnIsCalendarEnabledChanging(bool oldValue, bool newValue) => Messenger.Send(new CalendarEnableStatusChangedMessage(newValue));
|
||||
|
||||
private bool ShouldResetDayRanges(CalendarInitializeMessage message)
|
||||
{
|
||||
// Never reset if the initiative is from the app.
|
||||
if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false;
|
||||
|
||||
// 1. Display type is different.
|
||||
// 2. Day display count is different.
|
||||
// 3. Display date is not in the visible range.
|
||||
|
||||
return
|
||||
(_currentDisplayType != StatePersistanceService.CalendarDisplayType ||
|
||||
_displayDayCount != StatePersistanceService.DayDisplayCount ||
|
||||
(DayRanges != null && !DayRanges.Select(a => a.CalendarRenderOptions).Any(b => b.DateRange.IsInRange(message.DisplayDate))));
|
||||
}
|
||||
|
||||
public async void Receive(CalendarInitializeMessage message)
|
||||
{
|
||||
await _calendarLoadingSemaphore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
await ExecuteUIThread(() => IsCalendarEnabled = false);
|
||||
|
||||
if (ShouldResetDayRanges(message))
|
||||
{
|
||||
DayRanges.Clear();
|
||||
|
||||
Debug.WriteLine("Will reset day ranges.");
|
||||
}
|
||||
else if (ShouldScrollToItem(message))
|
||||
{
|
||||
// Scroll to the selected date.
|
||||
|
||||
Messenger.Send(new ScrollToDateMessage(message.DisplayDate));
|
||||
Debug.WriteLine("Scrolling to selected date.");
|
||||
return;
|
||||
}
|
||||
|
||||
// This will replace the whole collection because the user initiated a new render.
|
||||
await RenderDatesAsync(message.CalendarInitInitiative,
|
||||
message.DisplayDate,
|
||||
CalendarLoadDirection.Replace);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debugger.Break();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_calendarLoadingSemaphore.Release();
|
||||
|
||||
await ExecuteUIThread(() => IsCalendarEnabled = true);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RenderDatesAsync(CalendarInitInitiative calendarInitInitiative,
|
||||
DateTime? loadingDisplayDate = null,
|
||||
CalendarLoadDirection calendarLoadDirection = CalendarLoadDirection.Replace)
|
||||
{
|
||||
// This is the part we arrange the flip view calendar logic.
|
||||
|
||||
/* Loading for a month of the selected date is fine.
|
||||
* If the selected date is in the loaded range, we'll just change the selected flip index to scroll.
|
||||
* If the selected date is not in the loaded range:
|
||||
* 1. Detect the direction of the scroll.
|
||||
* 2. Load the next month.
|
||||
* 3. Replace existing month with the new month.
|
||||
*/
|
||||
|
||||
// 2 things are important: How many items should 1 flip have, and, where we should start loading?
|
||||
|
||||
// User initiated renders must always have a date to start with.
|
||||
if (calendarInitInitiative == CalendarInitInitiative.User) Guard.IsNotNull(loadingDisplayDate, nameof(loadingDisplayDate));
|
||||
|
||||
var strategy = GetDrawingStrategy(StatePersistanceService.CalendarDisplayType);
|
||||
var displayDate = loadingDisplayDate.GetValueOrDefault();
|
||||
|
||||
// How many days should be placed in 1 flip view item?
|
||||
int eachFlipItemCount = strategy.GetRenderDayCount(displayDate, StatePersistanceService.DayDisplayCount);
|
||||
|
||||
DateRange flipLoadRange = null;
|
||||
|
||||
if (calendarInitInitiative == CalendarInitInitiative.User)
|
||||
{
|
||||
flipLoadRange = strategy.GetRenderDateRange(displayDate, StatePersistanceService.DayDisplayCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
var minimumLoadedDate = DayRanges[0].CalendarRenderOptions.DateRange.StartDate;
|
||||
var maximumLoadedDate = DayRanges[DayRanges.Count - 1].CalendarRenderOptions.DateRange.EndDate;
|
||||
|
||||
var currentInitializedDateRange = new DateRange(minimumLoadedDate, maximumLoadedDate);
|
||||
|
||||
// App is trying to load.
|
||||
// This should be based on direction. We'll load the next or previous range.
|
||||
// DisplayDate is either the start or end date of the current visible range.
|
||||
|
||||
if (calendarLoadDirection == CalendarLoadDirection.Previous)
|
||||
{
|
||||
flipLoadRange = strategy.GetPreviousDateRange(currentInitializedDateRange, StatePersistanceService.DayDisplayCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
flipLoadRange = strategy.GetNextDateRange(currentInitializedDateRange, StatePersistanceService.DayDisplayCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Create day ranges for each flip item until we reach the total days to load.
|
||||
int totalFlipItemCount = (int)Math.Ceiling((double)flipLoadRange.TotalDays / eachFlipItemCount);
|
||||
|
||||
List<DayRangeRenderModel> renderModels = new();
|
||||
|
||||
for (int i = 0; i < totalFlipItemCount; i++)
|
||||
{
|
||||
var startDate = flipLoadRange.StartDate.AddDays(i * eachFlipItemCount);
|
||||
var endDate = startDate.AddDays(eachFlipItemCount);
|
||||
|
||||
var range = new DateRange(startDate, endDate);
|
||||
var renderOptions = new CalendarRenderOptions(range, _currentSettings);
|
||||
|
||||
renderModels.Add(new DayRangeRenderModel(renderOptions));
|
||||
}
|
||||
|
||||
CalendarLoadDirection animationDirection = calendarLoadDirection;
|
||||
|
||||
bool removeCurrent = calendarLoadDirection == CalendarLoadDirection.Replace;
|
||||
|
||||
if (calendarLoadDirection == CalendarLoadDirection.Replace)
|
||||
{
|
||||
// New date ranges are being replaced.
|
||||
// We must preserve existing selection if any, add the items before/after the current one, remove the current one.
|
||||
// This will make sure the new dates are animated in the correct direction.
|
||||
|
||||
isLoadMoreBlocked = true;
|
||||
|
||||
// Remove all other dates except this one.
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
DayRanges.RemoveRange(DayRanges.Where(a => a != SelectedDayRange).ToList());
|
||||
});
|
||||
|
||||
animationDirection = displayDate <= SelectedDayRange?.CalendarRenderOptions.DateRange.StartDate ?
|
||||
CalendarLoadDirection.Previous : CalendarLoadDirection.Next;
|
||||
}
|
||||
|
||||
if (animationDirection == CalendarLoadDirection.Next)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
foreach (var item in renderModels)
|
||||
{
|
||||
DayRanges.Add(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (animationDirection == CalendarLoadDirection.Previous)
|
||||
{
|
||||
// Wait for the animation to finish.
|
||||
// Otherwise it somehow shutters a little, which is annoying.
|
||||
|
||||
if (!removeCurrent) await Task.Delay(500);
|
||||
|
||||
// Insert each render model in reverse order.
|
||||
for (int i = renderModels.Count - 1; i >= 0; i--)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
DayRanges.Insert(0, renderModels[i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Flip count: ({DayRanges.Count})");
|
||||
|
||||
foreach (var item in DayRanges)
|
||||
{
|
||||
Debug.WriteLine($"- {item.CalendarRenderOptions.DateRange.ToString()}");
|
||||
}
|
||||
|
||||
if (removeCurrent)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
DayRanges.Remove(SelectedDayRange);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO...
|
||||
// await TryConsolidateItemsAsync();
|
||||
|
||||
isLoadMoreBlocked = false;
|
||||
|
||||
// Only scroll if the render is initiated by user.
|
||||
// Otherwise we'll scroll to the app rendered invisible date range.
|
||||
if (calendarInitInitiative == CalendarInitInitiative.User)
|
||||
{
|
||||
// Save the current settings for the page for later comparison.
|
||||
_currentDisplayType = StatePersistanceService.CalendarDisplayType;
|
||||
_displayDayCount = StatePersistanceService.DayDisplayCount;
|
||||
|
||||
Messenger.Send(new ScrollToDateMessage(displayDate));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TryConsolidateItemsAsync()
|
||||
{
|
||||
// Check if trimming is necessary
|
||||
if (DayRanges.Count > maxDayRangeSize)
|
||||
{
|
||||
Debug.WriteLine("Trimming items.");
|
||||
|
||||
isLoadMoreBlocked = true;
|
||||
|
||||
var removeCount = DayRanges.Count - maxDayRangeSize;
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
// Right shifted, remove from the start.
|
||||
if (SelectedDateRangeIndex > DayRanges.Count / 2)
|
||||
{
|
||||
DayRanges.RemoveRange(DayRanges.Take(removeCount).ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Left shifted, remove from the end.
|
||||
DayRanges.RemoveRange(DayRanges.Skip(DayRanges.Count - removeCount).Take(removeCount));
|
||||
}
|
||||
|
||||
SelectedDateRangeIndex = DayRanges.IndexOf(SelectedDayRange);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldScrollToItem(CalendarInitializeMessage message)
|
||||
{
|
||||
// Never scroll if the initiative is from the app.
|
||||
if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false;
|
||||
|
||||
// Nothing to scroll.
|
||||
if (DayRanges.Count == 0) return false;
|
||||
|
||||
var minimumLoadedDate = DayRanges[0].CalendarRenderOptions.DateRange.StartDate;
|
||||
var maximumLoadedDate = DayRanges[DayRanges.Count - 1].CalendarRenderOptions.DateRange.EndDate;
|
||||
|
||||
var selectedDate = message.DisplayDate;
|
||||
|
||||
return selectedDate >= minimumLoadedDate && selectedDate <= maximumLoadedDate;
|
||||
}
|
||||
|
||||
partial void OnSelectedDayRangeChanged(DayRangeRenderModel value)
|
||||
{
|
||||
if (DayRanges.Count == 0 || SelectedDateRangeIndex < 0) return;
|
||||
|
||||
if (isLoadMoreBlocked) return;
|
||||
|
||||
var selectedRange = DayRanges[SelectedDateRangeIndex];
|
||||
|
||||
if (selectedRange != null)
|
||||
{
|
||||
// Send the loading message initiated by the app.
|
||||
if (SelectedDateRangeIndex == DayRanges.Count - 1)
|
||||
{
|
||||
// Load next, starting from the end date.
|
||||
_ = LoadMoreAsync(CalendarLoadDirection.Next);
|
||||
}
|
||||
else if (SelectedDateRangeIndex == 0)
|
||||
{
|
||||
// Load previous, starting from the start date.
|
||||
|
||||
_ = LoadMoreAsync(CalendarLoadDirection.Previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadMoreAsync(CalendarLoadDirection direction)
|
||||
{
|
||||
Debug.WriteLine($"Loading {direction} items.");
|
||||
|
||||
try
|
||||
{
|
||||
await _calendarLoadingSemaphore.WaitAsync();
|
||||
await RenderDatesAsync(CalendarInitInitiative.App, calendarLoadDirection: direction);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debugger.Break();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_calendarLoadingSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnCalendarEventAdded(ICalendarItem calendarItem)
|
||||
{
|
||||
base.OnCalendarEventAdded(calendarItem);
|
||||
|
||||
// Test
|
||||
var eventDays = DayRanges.SelectMany(a => a.CalendarDays).Where(b => b.Period.Start.Date == calendarItem.StartTime.Date);
|
||||
|
||||
var beforeAllDay = new CalendarItem(calendarItem.StartTime.Date.AddHours(0), calendarItem.StartTime.Date.AddMinutes(30))
|
||||
{
|
||||
Name = "kj"
|
||||
};
|
||||
|
||||
var allday = new CalendarItem(calendarItem.StartTime.Date.AddHours(1), calendarItem.StartTime.Date.AddHours(10).AddMinutes(59))
|
||||
{
|
||||
Name = "All day"
|
||||
};
|
||||
|
||||
var test = new CalendarItem(calendarItem.StartTime.Date.AddHours(4), calendarItem.StartTime.Date.AddHours(4).AddMinutes(30))
|
||||
{
|
||||
Name = "test"
|
||||
};
|
||||
|
||||
var hour = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8))
|
||||
{
|
||||
Name = "1 h"
|
||||
};
|
||||
|
||||
var hourandhalf = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
|
||||
{
|
||||
Name = "1.5 h"
|
||||
};
|
||||
var halfhour1 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(7).AddMinutes(30))
|
||||
{
|
||||
Name = "30 min"
|
||||
};
|
||||
|
||||
var halfhour2 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7).AddMinutes(30), calendarItem.StartTime.Date.AddHours(8))
|
||||
{
|
||||
Name = "30 min"
|
||||
};
|
||||
var halfhour3 = new CalendarItem(calendarItem.StartTime.Date.AddHours(8), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
|
||||
{
|
||||
Name = "30 min"
|
||||
};
|
||||
|
||||
foreach (var day in eventDays)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
day.Events.Add(beforeAllDay);
|
||||
day.Events.Add(allday);
|
||||
day.Events.Add(hourandhalf);
|
||||
day.Events.Add(hour);
|
||||
day.Events.Add(halfhour1);
|
||||
day.Events.Add(halfhour2);
|
||||
day.Events.Add(halfhour3);
|
||||
day.Events.Add(test);
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Wino.Calendar.ViewModels/CalendarSettingsPageViewModel.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Translations;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.ViewModels
|
||||
{
|
||||
public partial class CalendarSettingsPageViewModel : CalendarBaseViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
private double _cellHourHeight;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedFirstDayOfWeekIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _is24HourHeaders;
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourStart;
|
||||
|
||||
[ObservableProperty]
|
||||
private TimeSpan _workingHourEnd;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<string> _dayNames = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _workingDayStartIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _workingDayEndIndex;
|
||||
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
private readonly bool _isLoaded = false;
|
||||
|
||||
public CalendarSettingsPageViewModel(IPreferencesService preferencesService)
|
||||
{
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
var currentLanguageLanguageCode = WinoTranslationDictionary.GetLanguageFileNameRelativePath(preferencesService.CurrentLanguage);
|
||||
|
||||
var cultureInfo = new CultureInfo(currentLanguageLanguageCode);
|
||||
|
||||
// Populate the day names list
|
||||
for (var i = 0; i < 7; i++)
|
||||
{
|
||||
_dayNames.Add(cultureInfo.DateTimeFormat.DayNames[i]);
|
||||
}
|
||||
|
||||
var cultureFirstDayName = cultureInfo.DateTimeFormat.GetDayName(preferencesService.FirstDayOfWeek);
|
||||
|
||||
_selectedFirstDayOfWeekIndex = _dayNames.IndexOf(cultureFirstDayName);
|
||||
_is24HourHeaders = preferencesService.Prefer24HourTimeFormat;
|
||||
_workingHourStart = preferencesService.WorkingHourStart;
|
||||
_workingHourEnd = preferencesService.WorkingHourEnd;
|
||||
_cellHourHeight = preferencesService.HourHeight;
|
||||
|
||||
_workingDayStartIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayStart));
|
||||
_workingDayEndIndex = _dayNames.IndexOf(cultureInfo.DateTimeFormat.GetDayName(preferencesService.WorkingDayEnd));
|
||||
|
||||
_isLoaded = true;
|
||||
}
|
||||
|
||||
partial void OnCellHourHeightChanged(double oldValue, double newValue) => SaveSettings();
|
||||
partial void OnIs24HourHeadersChanged(bool value) => SaveSettings();
|
||||
partial void OnSelectedFirstDayOfWeekIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingHourStartChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingHourEndChanged(TimeSpan value) => SaveSettings();
|
||||
partial void OnWorkingDayStartIndexChanged(int value) => SaveSettings();
|
||||
partial void OnWorkingDayEndIndexChanged(int value) => SaveSettings();
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
PreferencesService.FirstDayOfWeek = SelectedFirstDayOfWeekIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
PreferencesService.WorkingDayStart = WorkingDayStartIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
PreferencesService.WorkingDayEnd = WorkingDayEndIndex switch
|
||||
{
|
||||
0 => DayOfWeek.Sunday,
|
||||
1 => DayOfWeek.Monday,
|
||||
2 => DayOfWeek.Tuesday,
|
||||
3 => DayOfWeek.Wednesday,
|
||||
4 => DayOfWeek.Thursday,
|
||||
5 => DayOfWeek.Friday,
|
||||
6 => DayOfWeek.Saturday,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
PreferencesService.Prefer24HourTimeFormat = Is24HourHeaders;
|
||||
PreferencesService.WorkingHourStart = WorkingHourStart;
|
||||
PreferencesService.WorkingHourEnd = WorkingHourEnd;
|
||||
PreferencesService.HourHeight = CellHourHeight;
|
||||
|
||||
Messenger.Send(new CalendarSettingsUpdatedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Wino.Calendar.ViewModels/Wino.Calendar.ViewModels.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />
|
||||
<ProjectReference Include="..\Wino.Core.ViewModels\Wino.Core.ViewModels.csproj" />
|
||||
<ProjectReference Include="..\Wino.Core\Wino.Core.csproj" />
|
||||
<ProjectReference Include="..\Wino.Messages\Wino.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
Wino.Calendar/Activation/DefaultActivationHandler.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Wino.Activation;
|
||||
using Wino.Calendar.Views;
|
||||
|
||||
namespace Wino.Calendar.Activation
|
||||
{
|
||||
public class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
|
||||
{
|
||||
protected override Task HandleInternalAsync(IActivatedEventArgs args)
|
||||
{
|
||||
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Only navigate if Frame content doesn't exist.
|
||||
protected override bool CanHandleInternal(IActivatedEventArgs args)
|
||||
=> (Window.Current?.Content as Frame)?.Content == null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,34 @@
|
||||
<Application
|
||||
<core:WinoApplication
|
||||
x:Class="Wino.Calendar.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Wino.Calendar">
|
||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:core="using:Wino.Core.UWP"
|
||||
xmlns:coreStyles="using:Wino.Core.UWP.Styles"
|
||||
xmlns:local="using:Wino.Calendar"
|
||||
xmlns:styles="using:Wino.Calendar.Styles">
|
||||
<Application.Resources>
|
||||
<controls:XamlControlsResources>
|
||||
<controls:XamlControlsResources.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
|
||||
<core:CoreGeneric />
|
||||
|
||||
<!-- Calendar Specific -->
|
||||
<styles:WinoCalendarResources />
|
||||
|
||||
<ResourceDictionary Source="Styles/CalendarRenderStyles.xaml" />
|
||||
|
||||
<ResourceDictionary Source="Styles/CalendarDayItemsControl.xaml" />
|
||||
<ResourceDictionary Source="Styles/DayHeaderControl.xaml" />
|
||||
<ResourceDictionary Source="Styles/WinoDayTimelineCanvas.xaml" />
|
||||
<ResourceDictionary Source="Styles/DayColumnControl.xaml" />
|
||||
<ResourceDictionary Source="Styles/WinoCalendarView.xaml" />
|
||||
<ResourceDictionary Source="Styles/WinoCalendarTypeSelectorControl.xaml" />
|
||||
|
||||
<styles:CalendarItemControlResources />
|
||||
</controls:XamlControlsResources.MergedDictionaries>
|
||||
</controls:XamlControlsResources>
|
||||
</Application.Resources>
|
||||
</core:WinoApplication>
|
||||
|
||||
</Application>
|
||||
|
||||
@@ -1,100 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.ApplicationModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Wino.Activation;
|
||||
using Wino.Calendar.Activation;
|
||||
using Wino.Calendar.Services;
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
sealed partial class App : Application
|
||||
public sealed partial class App : WinoApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public override string AppCenterKey => "dfdad6ab-95f9-44cc-9112-45ec6730c49e";
|
||||
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Suspending += OnSuspending;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||
public override IServiceProvider ConfigureServices()
|
||||
{
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// Do not repeat app initialization when the Window already has content,
|
||||
// just ensure that the window is active
|
||||
if (rootFrame == null)
|
||||
services.RegisterCoreServices();
|
||||
services.RegisterCoreUWPServices();
|
||||
services.RegisterCoreViewModels();
|
||||
|
||||
RegisterUWPServices(services);
|
||||
RegisterViewModels(services);
|
||||
RegisterActivationHandlers(services);
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
#region Dependency Injection
|
||||
|
||||
private void RegisterActivationHandlers(IServiceCollection services)
|
||||
{
|
||||
//services.AddTransient<ProtocolActivationHandler>();
|
||||
//services.AddTransient<ToastNotificationActivationHandler>();
|
||||
//services.AddTransient<FileActivationHandler>();
|
||||
}
|
||||
|
||||
private void RegisterUWPServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<INavigationService, NavigationService>();
|
||||
services.AddSingleton<ICalendarDialogService, DialogService>();
|
||||
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
|
||||
}
|
||||
|
||||
private void RegisterViewModels(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(typeof(AppShellViewModel));
|
||||
services.AddTransient(typeof(CalendarPageViewModel));
|
||||
services.AddTransient(typeof(CalendarSettingsPageViewModel));
|
||||
services.AddTransient(typeof(AccountManagementViewModel));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
// TODO: Check server running.
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}");
|
||||
|
||||
if (!args.PrelaunchActivated)
|
||||
{
|
||||
// Create a Frame to act as the navigation context and navigate to the first page
|
||||
rootFrame = new Frame();
|
||||
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
|
||||
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
|
||||
{
|
||||
//TODO: Load state from previously suspended application
|
||||
}
|
||||
|
||||
// Place the frame in the current Window
|
||||
Window.Current.Content = rootFrame;
|
||||
}
|
||||
|
||||
if (e.PrelaunchActivated == false)
|
||||
{
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
}
|
||||
// Ensure the current window is active
|
||||
Window.Current.Activate();
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when Navigation to a certain page fails
|
||||
/// </summary>
|
||||
/// <param name="sender">The Frame which failed navigation</param>
|
||||
/// <param name="e">Details about the navigation failure</param>
|
||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
protected override IEnumerable<ActivationHandler> GetActivationHandlers()
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application execution is being suspended. Application state is saved
|
||||
/// without knowing whether the application will be terminated or resumed with the contents
|
||||
/// of memory still intact.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the suspend request.</param>
|
||||
/// <param name="e">Details about the suspend request.</param>
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
//TODO: Save application state and stop any background activity
|
||||
deferral.Complete();
|
||||
}
|
||||
protected override ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler()
|
||||
=> new DefaultActivationHandler();
|
||||
}
|
||||
}
|
||||
|
||||
41
Wino.Calendar/Args/TimelineCellSelectedArgs.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Wino.Calendar.Args
|
||||
{
|
||||
/// <summary>
|
||||
/// When a new timeline cell is selected.
|
||||
/// </summary>
|
||||
public class TimelineCellSelectedArgs : EventArgs
|
||||
{
|
||||
public TimelineCellSelectedArgs(DateTime clickedDate, Point canvasPoint, Point positionerPoint, Size cellSize)
|
||||
{
|
||||
ClickedDate = clickedDate;
|
||||
CanvasPoint = canvasPoint;
|
||||
PositionerPoint = positionerPoint;
|
||||
CellSize = cellSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clicked date and time information for the cell.
|
||||
/// </summary>
|
||||
public DateTime ClickedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position relative to the cell drawing part of the canvas.
|
||||
/// Used to detect clicked cell from the position.
|
||||
/// </summary>
|
||||
public Point CanvasPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Position relative to the main root positioner element of the drawing canvas.
|
||||
/// Used to show the create event dialog teaching tip in correct position.
|
||||
/// </summary>
|
||||
public Point PositionerPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the cell.
|
||||
/// </summary>
|
||||
public Size CellSize { get; }
|
||||
}
|
||||
}
|
||||
9
Wino.Calendar/Args/TimelineCellUnselectedArgs.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Calendar.Args
|
||||
{
|
||||
/// <summary>
|
||||
/// When selected timeline cell is unselected.
|
||||
/// </summary>
|
||||
public class TimelineCellUnselectedArgs : EventArgs { }
|
||||
}
|
||||
BIN
Wino.Calendar/Assets/LargeTile.scale-100.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
Wino.Calendar/Assets/LargeTile.scale-125.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
Wino.Calendar/Assets/LargeTile.scale-150.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
Wino.Calendar/Assets/LargeTile.scale-200.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
Wino.Calendar/Assets/LargeTile.scale-400.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
Wino.Calendar/Assets/SmallTile.scale-100.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Wino.Calendar/Assets/SmallTile.scale-125.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Wino.Calendar/Assets/SmallTile.scale-150.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
Wino.Calendar/Assets/SmallTile.scale-200.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
Wino.Calendar/Assets/SmallTile.scale-400.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
Wino.Calendar/Assets/SplashScreen.scale-100.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
Wino.Calendar/Assets/SplashScreen.scale-125.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
Wino.Calendar/Assets/SplashScreen.scale-150.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 17 KiB |
BIN
Wino.Calendar/Assets/SplashScreen.scale-400.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
Wino.Calendar/Assets/Square150x150Logo.scale-100.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
Wino.Calendar/Assets/Square150x150Logo.scale-125.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Wino.Calendar/Assets/Square150x150Logo.scale-150.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 6.0 KiB |
BIN
Wino.Calendar/Assets/Square150x150Logo.scale-400.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 809 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 809 B |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.scale-100.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.scale-125.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.scale-150.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.2 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.scale-400.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.targetsize-16.png
Normal file
|
After Width: | Height: | Size: 596 B |
BIN
Wino.Calendar/Assets/Square44x44Logo.targetsize-24.png
Normal file
|
After Width: | Height: | Size: 920 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.targetsize-256.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.targetsize-32.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Wino.Calendar/Assets/Square44x44Logo.targetsize-48.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
Wino.Calendar/Assets/StoreLogo.scale-100.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
Wino.Calendar/Assets/StoreLogo.scale-125.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
Wino.Calendar/Assets/StoreLogo.scale-150.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
Wino.Calendar/Assets/StoreLogo.scale-200.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
Wino.Calendar/Assets/StoreLogo.scale-400.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Wino.Calendar/Assets/Wide310x150Logo.scale-100.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Wino.Calendar/Assets/Wide310x150Logo.scale-125.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
Wino.Calendar/Assets/Wide310x150Logo.scale-150.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 6.6 KiB |
BIN
Wino.Calendar/Assets/Wide310x150Logo.scale-400.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
132
Wino.Calendar/Controls/CalendarDayItemsControl.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class CalendarDayItemsControl : Control
|
||||
{
|
||||
private const string PART_CalendarPanel = nameof(PART_CalendarPanel);
|
||||
|
||||
private WinoCalendarPanel CalendarPanel;
|
||||
|
||||
public CalendarDayModel DayModel
|
||||
{
|
||||
get { return (CalendarDayModel)GetValue(DayModelProperty); }
|
||||
set { SetValue(DayModelProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(CalendarDayItemsControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRepresentingDateChanged)));
|
||||
|
||||
private static void OnRepresentingDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarDayItemsControl control)
|
||||
{
|
||||
if (e.OldValue != null && e.OldValue is CalendarDayModel oldCalendarDayModel)
|
||||
{
|
||||
control.DetachCollection(oldCalendarDayModel.Events);
|
||||
}
|
||||
|
||||
if (e.NewValue != null && e.NewValue is CalendarDayModel newCalendarDayModel)
|
||||
{
|
||||
control.AttachCollection(newCalendarDayModel.Events);
|
||||
}
|
||||
|
||||
control.ResetItems();
|
||||
control.RenderEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public CalendarDayItemsControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(CalendarDayItemsControl);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
CalendarPanel = GetTemplateChild(PART_CalendarPanel) as WinoCalendarPanel;
|
||||
|
||||
RenderEvents();
|
||||
}
|
||||
|
||||
private void ResetItems()
|
||||
{
|
||||
if (CalendarPanel == null) return;
|
||||
|
||||
CalendarPanel.Children.Clear();
|
||||
}
|
||||
|
||||
private void RenderEvents()
|
||||
{
|
||||
if (CalendarPanel == null || CalendarPanel.DayModel == null) return;
|
||||
|
||||
RenderCalendarItems();
|
||||
}
|
||||
|
||||
private void AttachCollection(ObservableCollection<ICalendarItem> newCollection)
|
||||
=> newCollection.CollectionChanged += CalendarItemsChanged;
|
||||
|
||||
private void DetachCollection(ObservableCollection<ICalendarItem> oldCollection)
|
||||
=> oldCollection.CollectionChanged -= CalendarItemsChanged;
|
||||
|
||||
private void CalendarItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
foreach (ICalendarItem item in e.NewItems)
|
||||
{
|
||||
AddItem(item);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
foreach (ICalendarItem item in e.OldItems)
|
||||
{
|
||||
var control = GetCalendarItemControl(item);
|
||||
if (control != null)
|
||||
{
|
||||
CalendarPanel.Children.Remove(control);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
ResetItems();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private CalendarItemControl GetCalendarItemControl(ICalendarItem item)
|
||||
=> CalendarPanel.Children.Where(c => c is CalendarItemControl calendarItemControl && calendarItemControl.Item == item).FirstOrDefault() as CalendarItemControl;
|
||||
|
||||
private void RenderCalendarItems()
|
||||
{
|
||||
if (DayModel == null || DayModel.Events == null || DayModel.Events.Count == 0)
|
||||
{
|
||||
ResetItems();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in DayModel.Events)
|
||||
{
|
||||
AddItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddItem(ICalendarItem item)
|
||||
{
|
||||
CalendarPanel.Children.Add(new CalendarItemControl()
|
||||
{
|
||||
Item = item
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Wino.Calendar/Controls/CalendarItemControl.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class CalendarItemControl : Control
|
||||
{
|
||||
public ICalendarItem Item
|
||||
{
|
||||
get { return (ICalendarItem)GetValue(ItemProperty); }
|
||||
set { SetValue(ItemProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(ICalendarItem), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
|
||||
|
||||
public CalendarItemControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(CalendarItemControl);
|
||||
}
|
||||
|
||||
private static void OnItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CalendarItemControl control)
|
||||
{
|
||||
control.UpdateDateRendering();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDateRendering()
|
||||
{
|
||||
if (Item == null) return;
|
||||
|
||||
UpdateLayout();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Item?.Name ?? "NA";
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Wino.Calendar/Controls/CustomCalendarFlipView.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// FlipView that hides the navigation buttons and exposes methods to navigate to the next and previous items with animations.
|
||||
/// </summary>
|
||||
public class CustomCalendarFlipView : FlipView
|
||||
{
|
||||
private const string PART_PreviousButton = "PreviousButtonHorizontal";
|
||||
private const string PART_NextButton = "NextButtonHorizontal";
|
||||
|
||||
private Button PreviousButton;
|
||||
private Button NextButton;
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
PreviousButton = GetTemplateChild(PART_PreviousButton) as Button;
|
||||
NextButton = GetTemplateChild(PART_NextButton) as Button;
|
||||
|
||||
// Hide navigation buttons
|
||||
PreviousButton.Opacity = NextButton.Opacity = 0;
|
||||
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
|
||||
}
|
||||
|
||||
public void GoPreviousFlip()
|
||||
{
|
||||
var backPeer = new ButtonAutomationPeer(PreviousButton);
|
||||
backPeer.Invoke();
|
||||
}
|
||||
|
||||
public void GoNextFlip()
|
||||
{
|
||||
var nextPeer = new ButtonAutomationPeer(NextButton);
|
||||
nextPeer.Invoke();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
65
Wino.Calendar/Controls/DayColumnControl.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public 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 TodayState = nameof(TodayState);
|
||||
private const string NotTodayState = nameof(NotTodayState);
|
||||
|
||||
private TextBlock HeaderDateDayText;
|
||||
private TextBlock ColumnHeaderText;
|
||||
private Border IsTodayBorder;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
HeaderDateDayText = GetTemplateChild(PART_HeaderDateDayText) as TextBlock;
|
||||
ColumnHeaderText = GetTemplateChild(PART_ColumnHeaderText) as TextBlock;
|
||||
IsTodayBorder = GetTemplateChild(PART_IsTodayBorder) as Border;
|
||||
|
||||
UpdateValues();
|
||||
}
|
||||
|
||||
private static void OnRenderingPropertiesChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (control is DayColumnControl columnControl)
|
||||
{
|
||||
columnControl.UpdateValues();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
if (HeaderDateDayText == null || ColumnHeaderText == null || IsTodayBorder == null || DayModel == null) return;
|
||||
|
||||
HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString();
|
||||
ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo);
|
||||
|
||||
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;
|
||||
|
||||
VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Wino.Calendar/Controls/DayHeaderControl.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class DayHeaderControl : Control
|
||||
{
|
||||
private const string PART_DayHeaderTextBlock = nameof(PART_DayHeaderTextBlock);
|
||||
private TextBlock HeaderTextblock;
|
||||
|
||||
public DayHeaderDisplayType DisplayType
|
||||
{
|
||||
get { return (DayHeaderDisplayType)GetValue(DisplayTypeProperty); }
|
||||
set { SetValue(DisplayTypeProperty, value); }
|
||||
}
|
||||
|
||||
public DateTime Date
|
||||
{
|
||||
get { return (DateTime)GetValue(DateProperty); }
|
||||
set { SetValue(DateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DateProperty = DependencyProperty.Register(nameof(Date), typeof(DateTime), typeof(DayHeaderControl), new PropertyMetadata(default(DateTime), new PropertyChangedCallback(OnHeaderPropertyChanged)));
|
||||
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register(nameof(DisplayType), typeof(DayHeaderDisplayType), typeof(DayHeaderControl), new PropertyMetadata(DayHeaderDisplayType.TwentyFourHour, new PropertyChangedCallback(OnHeaderPropertyChanged)));
|
||||
|
||||
public DayHeaderControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(DayHeaderControl);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
HeaderTextblock = GetTemplateChild(PART_DayHeaderTextBlock) as TextBlock;
|
||||
UpdateHeaderText();
|
||||
}
|
||||
|
||||
private static void OnHeaderPropertyChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (control is DayHeaderControl headerControl)
|
||||
{
|
||||
headerControl.UpdateHeaderText();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHeaderText()
|
||||
{
|
||||
if (HeaderTextblock != null)
|
||||
{
|
||||
HeaderTextblock.Text = DisplayType == DayHeaderDisplayType.TwelveHour ? Date.ToString("h tt") : Date.ToString("HH:mm");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
144
Wino.Calendar/Controls/WinoCalendarControl.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
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
|
||||
{
|
||||
public class WinoCalendarControl : Control
|
||||
{
|
||||
private const string PART_WinoFlipView = nameof(PART_WinoFlipView);
|
||||
|
||||
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
|
||||
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
|
||||
|
||||
#region Dependency Properties
|
||||
|
||||
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 DayRangeRenderModel SelectedFlipViewDayRange
|
||||
{
|
||||
get { return (DayRangeRenderModel)GetValue(SelectedFlipViewDayRangeProperty); }
|
||||
set { SetValue(SelectedFlipViewDayRangeProperty, 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.
|
||||
/// </summary>
|
||||
public ObservableCollection<DayRangeRenderModel> DayRanges
|
||||
{
|
||||
get { return (ObservableCollection<DayRangeRenderModel>)GetValue(DayRangesProperty); }
|
||||
set { SetValue(DayRangesProperty, value); }
|
||||
}
|
||||
|
||||
public int SelectedFlipViewIndex
|
||||
{
|
||||
get { return (int)GetValue(SelectedFlipViewIndexProperty); }
|
||||
set { SetValue(SelectedFlipViewIndexProperty, value); }
|
||||
}
|
||||
|
||||
#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()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarControl);
|
||||
SizeChanged += CalendarSizeChanged;
|
||||
}
|
||||
|
||||
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (ActiveCanvas == null) return;
|
||||
|
||||
ActiveCanvas.SelectedDateTime = null;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
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)
|
||||
{
|
||||
ActiveCanvas = e;
|
||||
|
||||
SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
|
||||
}
|
||||
|
||||
private void ActiveTimelineCellUnselected(object sender, TimelineCellUnselectedArgs e)
|
||||
=> TimelineCellUnselected?.Invoke(this, e);
|
||||
|
||||
private void ActiveTimelineCellSelected(object sender, TimelineCellSelectedArgs e)
|
||||
=> TimelineCellSelected?.Invoke(this, e);
|
||||
|
||||
public void NavigateToDay(DateTime dateTime) => InternalFlipView.NavigateToDay(dateTime);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Wino.Calendar/Controls/WinoCalendarFlipView.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Core.MenuItems;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class WinoCalendarFlipView : CustomCalendarFlipView
|
||||
{
|
||||
public event EventHandler<WinoDayTimelineCanvas> ActiveTimelineCanvasChanged;
|
||||
|
||||
public WinoCalendarFlipView()
|
||||
{
|
||||
SelectionChanged += CalendarDisplayRangeChanged;
|
||||
}
|
||||
|
||||
private async void CalendarDisplayRangeChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (SelectedIndex < 0)
|
||||
ActiveTimelineCanvasChanged?.Invoke(this, null);
|
||||
else
|
||||
{
|
||||
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
|
||||
while (ContainerFromIndex(SelectedIndex) == null)
|
||||
{
|
||||
await Task.Delay(250);
|
||||
}
|
||||
|
||||
if (ContainerFromIndex(SelectedIndex) is FlipViewItem flipViewItem)
|
||||
{
|
||||
var canvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
|
||||
ActiveTimelineCanvasChanged?.Invoke(this, canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the specified date in the calendar.
|
||||
/// </summary>
|
||||
/// <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));
|
||||
|
||||
if (dayRange != null)
|
||||
{
|
||||
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
|
||||
|
||||
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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.
|
||||
// Find the day range that contains the hour.
|
||||
}
|
||||
|
||||
private ObservableRangeCollection<DayRangeRenderModel> GetItemsSource()
|
||||
=> ItemsSource as ObservableRangeCollection<DayRangeRenderModel>;
|
||||
}
|
||||
}
|
||||
254
Wino.Calendar/Controls/WinoCalendarPanel.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Calendar.Models;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class WinoCalendarPanel : Panel
|
||||
{
|
||||
private const double LastItemRightExtraMargin = 12d;
|
||||
|
||||
// Store each ICalendarItem measurements by their Id.
|
||||
private readonly Dictionary<Guid, CalendarItemMeasurement> _measurements = new Dictionary<Guid, CalendarItemMeasurement>();
|
||||
|
||||
public static readonly DependencyProperty EventItemMarginProperty = DependencyProperty.Register(nameof(EventItemMargin), typeof(Thickness), typeof(WinoCalendarPanel), new PropertyMetadata(new Thickness(0, 0, 0, 0)));
|
||||
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(WinoCalendarPanel), new PropertyMetadata(null, new PropertyChangedCallback(OnDayChanged)));
|
||||
|
||||
public CalendarDayModel DayModel
|
||||
{
|
||||
get { return (CalendarDayModel)GetValue(DayModelProperty); }
|
||||
set { SetValue(DayModelProperty, value); }
|
||||
}
|
||||
|
||||
public Thickness EventItemMargin
|
||||
{
|
||||
get { return (Thickness)GetValue(EventItemMarginProperty); }
|
||||
set { SetValue(EventItemMarginProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnDayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoCalendarPanel control)
|
||||
{
|
||||
// We need to listen for new events being added or removed from the collection to reset measurements.
|
||||
if (e.OldValue is CalendarDayModel oldDayModel)
|
||||
{
|
||||
control.DetachCollection(oldDayModel.Events);
|
||||
}
|
||||
|
||||
if (e.NewValue is CalendarDayModel newDayModel)
|
||||
{
|
||||
control.AttachCollection(newDayModel.Events);
|
||||
}
|
||||
|
||||
control.ResetMeasurements();
|
||||
control.UpdateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachCollection(IEnumerable<ICalendarItem> events)
|
||||
{
|
||||
if (events is INotifyCollectionChanged collection)
|
||||
{
|
||||
collection.CollectionChanged += EventCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void DetachCollection(IEnumerable<ICalendarItem> events)
|
||||
{
|
||||
if (events is INotifyCollectionChanged collection)
|
||||
{
|
||||
collection.CollectionChanged -= EventCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetMeasurements() => _measurements.Clear();
|
||||
|
||||
// No need to handle actions. Each action requires a full measurement update.
|
||||
private void EventCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => ResetMeasurements();
|
||||
|
||||
private double GetChildTopMargin(DateTime childStart, double availableHeight)
|
||||
{
|
||||
double totalMinutes = 1440;
|
||||
double minutesFromStart = (childStart - DayModel.RepresentingDate).TotalMinutes;
|
||||
return (minutesFromStart / totalMinutes) * availableHeight;
|
||||
}
|
||||
|
||||
private double GetChildWidth(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||
{
|
||||
return (calendarItemMeasurement.Right - calendarItemMeasurement.Left) * availableWidth;
|
||||
}
|
||||
|
||||
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||
=> availableWidth * calendarItemMeasurement.Left;
|
||||
|
||||
private double GetChildHeight(DateTime childStart, DateTime childEnd)
|
||||
{
|
||||
double totalMinutes = 1440;
|
||||
double availableHeight = DayModel.CalendarRenderOptions.CalendarSettings.HourHeight * 24;
|
||||
double childDuration = (childEnd - childStart).TotalMinutes;
|
||||
return (childDuration / totalMinutes) * availableHeight;
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
// Measure/arrange each child height and width.
|
||||
// This is a vertical calendar. Therefore the height of each child is the duration of the event.
|
||||
// Children weights for left and right will be saved if they don't exist.
|
||||
// This is important because we don't want to measure the weights again.
|
||||
// They don't change until new event is added or removed.
|
||||
// Width of the each child may depend on the rectangle packing algorithm.
|
||||
// Children are first categorized into columns. Then each column is shifted to the left until
|
||||
// no overlap occurs. The width of each child is calculated based on the number of columns it spans.
|
||||
|
||||
double availableHeight = finalSize.Height;
|
||||
double availableWidth = finalSize.Width;
|
||||
|
||||
var calendarControls = Children.Cast<CalendarItemControl>();
|
||||
|
||||
if (_measurements.Count == 0 && DayModel.Events.Count > 0)
|
||||
{
|
||||
// We keep track of this collection when event is added/removed/reset etc.
|
||||
// So if the collection is empty, we must fill it up again for proper calculations.
|
||||
|
||||
LayoutEvents(DayModel.Events);
|
||||
}
|
||||
|
||||
foreach (var child in calendarControls)
|
||||
{
|
||||
// We can't arrange this child. It doesn't have a valid ICalendarItem or measurement.
|
||||
if (child.Item == null || !_measurements.ContainsKey(child.Item.Id)) continue;
|
||||
|
||||
var childMeasurement = _measurements[child.Item.Id];
|
||||
|
||||
double childHeight = Math.Max(0, GetChildHeight(child.Item.StartTime, child.Item.EndTime));
|
||||
double childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
|
||||
double childTop = Math.Max(0, GetChildTopMargin(child.Item.StartTime, availableHeight));
|
||||
double childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
|
||||
|
||||
bool isHorizontallyLastItem = childMeasurement.Right == 1;
|
||||
|
||||
// Add additional right margin to items that falls on the right edge of the panel.
|
||||
// Max of 5% of the width or 20px.
|
||||
var extraRightMargin = isHorizontallyLastItem ? Math.Max(LastItemRightExtraMargin, finalSize.Width * 5 / 100) : 0;
|
||||
|
||||
var finalChildWidth = childWidth - extraRightMargin;
|
||||
|
||||
if (finalChildWidth < 0) finalChildWidth = 1;
|
||||
|
||||
child.Measure(new Size(childWidth, childHeight));
|
||||
|
||||
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, childWidth - extraRightMargin, childHeight);
|
||||
|
||||
child.Arrange(arrangementRect);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
#region ColumSpanning and Packing Algorithm
|
||||
|
||||
private void AddOrUpdateMeasurement(ICalendarItem calendarItem, CalendarItemMeasurement measurement)
|
||||
{
|
||||
if (_measurements.ContainsKey(calendarItem.Id))
|
||||
{
|
||||
_measurements[calendarItem.Id] = measurement;
|
||||
}
|
||||
else
|
||||
{
|
||||
_measurements.Add(calendarItem.Id, measurement);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the left and right positions of each event, such that there are no overlap.
|
||||
private void LayoutEvents(IEnumerable<ICalendarItem> events)
|
||||
{
|
||||
var columns = new List<List<ICalendarItem>>();
|
||||
DateTime? lastEventEnding = null;
|
||||
|
||||
foreach (var ev in events.OrderBy(ev => ev.Period.Start).ThenBy(ev => ev.Period.End))
|
||||
{
|
||||
if (ev.Period.Start >= lastEventEnding)
|
||||
{
|
||||
PackEvents(columns);
|
||||
columns.Clear();
|
||||
lastEventEnding = null;
|
||||
}
|
||||
|
||||
bool placed = false;
|
||||
|
||||
foreach (var col in columns)
|
||||
{
|
||||
if (!col.Last().Period.OverlapsWith(ev.Period))
|
||||
{
|
||||
col.Add(ev);
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!placed)
|
||||
{
|
||||
columns.Add(new List<ICalendarItem> { ev });
|
||||
}
|
||||
if (lastEventEnding == null || ev.Period.End > lastEventEnding.Value)
|
||||
{
|
||||
lastEventEnding = ev.Period.End;
|
||||
}
|
||||
}
|
||||
if (columns.Count > 0)
|
||||
{
|
||||
PackEvents(columns);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the left and right positions for each event in the connected group.
|
||||
private void PackEvents(List<List<ICalendarItem>> columns)
|
||||
{
|
||||
float numColumns = columns.Count;
|
||||
int iColumn = 0;
|
||||
|
||||
foreach (var col in columns)
|
||||
{
|
||||
foreach (var ev in col)
|
||||
{
|
||||
int colSpan = ExpandEvent(ev, iColumn, columns);
|
||||
|
||||
var leftWeight = iColumn / numColumns;
|
||||
var rightWeight = (iColumn + colSpan) / numColumns;
|
||||
|
||||
AddOrUpdateMeasurement(ev, new CalendarItemMeasurement(leftWeight, rightWeight));
|
||||
}
|
||||
|
||||
iColumn++;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks how many columns the event can expand into, without colliding with other events.
|
||||
private int ExpandEvent(ICalendarItem ev, int iColumn, List<List<ICalendarItem>> columns)
|
||||
{
|
||||
int colSpan = 1;
|
||||
|
||||
foreach (var col in columns.Skip(iColumn + 1))
|
||||
{
|
||||
foreach (var ev1 in col)
|
||||
{
|
||||
if (ev1.Period.OverlapsWith(ev.Period)) return colSpan;
|
||||
}
|
||||
|
||||
colSpan++;
|
||||
}
|
||||
|
||||
return colSpan;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
92
Wino.Calendar/Controls/WinoCalendarTypeSelectorControl.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class WinoCalendarTypeSelectorControl : Control
|
||||
{
|
||||
private const string PART_TodayButton = nameof(PART_TodayButton);
|
||||
private const string PART_DayToggle = nameof(PART_DayToggle);
|
||||
private const string PART_WeekToggle = nameof(PART_WeekToggle);
|
||||
private const string PART_MonthToggle = nameof(PART_MonthToggle);
|
||||
private const string PART_YearToggle = nameof(PART_YearToggle);
|
||||
|
||||
public static readonly DependencyProperty SelectedTypeProperty = DependencyProperty.Register(nameof(SelectedType), typeof(CalendarDisplayType), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(CalendarDisplayType.Week));
|
||||
public static readonly DependencyProperty DisplayDayCountProperty = DependencyProperty.Register(nameof(DisplayDayCount), typeof(int), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(0));
|
||||
public static readonly DependencyProperty TodayClickedCommandProperty = DependencyProperty.Register(nameof(TodayClickedCommand), typeof(ICommand), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(null));
|
||||
|
||||
public ICommand TodayClickedCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(TodayClickedCommandProperty); }
|
||||
set { SetValue(TodayClickedCommandProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarDisplayType SelectedType
|
||||
{
|
||||
get { return (CalendarDisplayType)GetValue(SelectedTypeProperty); }
|
||||
set { SetValue(SelectedTypeProperty, value); }
|
||||
}
|
||||
|
||||
public int DisplayDayCount
|
||||
{
|
||||
get { return (int)GetValue(DisplayDayCountProperty); }
|
||||
set { SetValue(DisplayDayCountProperty, value); }
|
||||
}
|
||||
|
||||
private AppBarButton _todayButton;
|
||||
private AppBarToggleButton _dayToggle;
|
||||
private AppBarToggleButton _weekToggle;
|
||||
private AppBarToggleButton _monthToggle;
|
||||
private AppBarToggleButton _yearToggle;
|
||||
|
||||
public WinoCalendarTypeSelectorControl()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoCalendarTypeSelectorControl);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_todayButton = GetTemplateChild(PART_TodayButton) as AppBarButton;
|
||||
_dayToggle = GetTemplateChild(PART_DayToggle) as AppBarToggleButton;
|
||||
_weekToggle = GetTemplateChild(PART_WeekToggle) as AppBarToggleButton;
|
||||
_monthToggle = GetTemplateChild(PART_MonthToggle) as AppBarToggleButton;
|
||||
_yearToggle = GetTemplateChild(PART_YearToggle) as AppBarToggleButton;
|
||||
|
||||
Guard.IsNotNull(_todayButton, nameof(_todayButton));
|
||||
Guard.IsNotNull(_dayToggle, nameof(_dayToggle));
|
||||
Guard.IsNotNull(_weekToggle, nameof(_weekToggle));
|
||||
Guard.IsNotNull(_monthToggle, nameof(_monthToggle));
|
||||
Guard.IsNotNull(_yearToggle, nameof(_yearToggle));
|
||||
|
||||
_todayButton.Click += TodayClicked;
|
||||
|
||||
_dayToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Day); };
|
||||
_weekToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Week); };
|
||||
_monthToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Month); };
|
||||
_yearToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Year); };
|
||||
|
||||
UpdateToggleButtonStates();
|
||||
}
|
||||
|
||||
private void TodayClicked(object sender, RoutedEventArgs e) => TodayClickedCommand?.Execute(null);
|
||||
|
||||
private void SetSelectedType(CalendarDisplayType type)
|
||||
{
|
||||
SelectedType = type;
|
||||
UpdateToggleButtonStates();
|
||||
}
|
||||
|
||||
private void UpdateToggleButtonStates()
|
||||
{
|
||||
_dayToggle.IsChecked = SelectedType == CalendarDisplayType.Day;
|
||||
_weekToggle.IsChecked = SelectedType == CalendarDisplayType.Week;
|
||||
_monthToggle.IsChecked = SelectedType == CalendarDisplayType.Month;
|
||||
_yearToggle.IsChecked = SelectedType == CalendarDisplayType.Year;
|
||||
}
|
||||
}
|
||||
}
|
||||
145
Wino.Calendar/Controls/WinoCalendarView.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
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 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 TodayBackgroundBrushProperty = DependencyProperty.Register(nameof(TodayBackgroundBrush), typeof(Brush), typeof(WinoCalendarView), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty DateClickedCommandProperty = DependencyProperty.Register(nameof(DateClickedCommand), typeof(ICommand), typeof(WinoCalendarView), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the command to execute when a date is picked.
|
||||
/// Unused.
|
||||
/// </summary>
|
||||
public ICommand DateClickedCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(DateClickedCommandProperty); }
|
||||
set { SetValue(DateClickedCommandProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the highlighted range of dates.
|
||||
/// </summary>
|
||||
public DateRange HighlightedDateRange
|
||||
{
|
||||
get { return (DateRange)GetValue(HighlightedDateRangeProperty); }
|
||||
set { SetValue(HighlightedDateRangeProperty, value); }
|
||||
}
|
||||
|
||||
public Brush VisibleDateBackground
|
||||
{
|
||||
get { return (Brush)GetValue(VisibleDateBackgroundProperty); }
|
||||
set { SetValue(VisibleDateBackgroundProperty, value); }
|
||||
}
|
||||
|
||||
public Brush TodayBackgroundBrush
|
||||
{
|
||||
get { return (Brush)GetValue(TodayBackgroundBrushProperty); }
|
||||
set { SetValue(TodayBackgroundBrushProperty, 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 || TodayBackgroundBrush == null || CalendarView == null) return;
|
||||
|
||||
var markDateCalendarDayItems = WinoVisualTreeHelper.FindDescendants<CalendarViewDayItem>(CalendarView);
|
||||
|
||||
foreach (var calendarDayItem in markDateCalendarDayItems)
|
||||
{
|
||||
var border = WinoVisualTreeHelper.GetChildObject<Border>(calendarDayItem, PART_DayViewItemBorder);
|
||||
|
||||
if (border == null) return;
|
||||
|
||||
if (calendarDayItem.Date.Date == DateTime.Today.Date)
|
||||
{
|
||||
border.Background = TodayBackgroundBrush;
|
||||
}
|
||||
else if (calendarDayItem.Date.Date >= HighlightedDateRange.StartDate.Date && calendarDayItem.Date.Date < HighlightedDateRange.EndDate.Date)
|
||||
{
|
||||
border.Background = VisibleDateBackground;
|
||||
}
|
||||
else
|
||||
{
|
||||
border.Background = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
277
Wino.Calendar/Controls/WinoDayTimelineCanvas.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Input;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Wino.Calendar.Args;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls
|
||||
{
|
||||
public class WinoDayTimelineCanvas : Control, IDisposable
|
||||
{
|
||||
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
|
||||
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
|
||||
|
||||
private const string PART_InternalCanvas = nameof(PART_InternalCanvas);
|
||||
private CanvasControl Canvas;
|
||||
|
||||
public static readonly DependencyProperty RenderOptionsProperty = DependencyProperty.Register(nameof(RenderOptions), typeof(CalendarRenderOptions), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SeperatorColorProperty = DependencyProperty.Register(nameof(SeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty HalfHourSeperatorColorProperty = DependencyProperty.Register(nameof(HalfHourSeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SelectedCellBackgroundBrushProperty = DependencyProperty.Register(nameof(SelectedCellBackgroundBrush), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty WorkingHourCellBackgroundColorProperty = DependencyProperty.Register(nameof(WorkingHourCellBackgroundColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
|
||||
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(nameof(SelectedDateTime), typeof(DateTime?), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedDateTimeChanged)));
|
||||
public static readonly DependencyProperty PositionerUIElementProperty = DependencyProperty.Register(nameof(PositionerUIElement), typeof(UIElement), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null));
|
||||
|
||||
public UIElement PositionerUIElement
|
||||
{
|
||||
get { return (UIElement)GetValue(PositionerUIElementProperty); }
|
||||
set { SetValue(PositionerUIElementProperty, value); }
|
||||
}
|
||||
|
||||
public CalendarRenderOptions RenderOptions
|
||||
{
|
||||
get { return (CalendarRenderOptions)GetValue(RenderOptionsProperty); }
|
||||
set { SetValue(RenderOptionsProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush HalfHourSeperatorColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(HalfHourSeperatorColorProperty); }
|
||||
set { SetValue(HalfHourSeperatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush SeperatorColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SeperatorColorProperty); }
|
||||
set { SetValue(SeperatorColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush WorkingHourCellBackgroundColor
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(WorkingHourCellBackgroundColorProperty); }
|
||||
set { SetValue(WorkingHourCellBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public SolidColorBrush SelectedCellBackgroundBrush
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SelectedCellBackgroundBrushProperty); }
|
||||
set { SetValue(SelectedCellBackgroundBrushProperty, value); }
|
||||
}
|
||||
|
||||
public DateTime? SelectedDateTime
|
||||
{
|
||||
get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
|
||||
set { SetValue(SelectedDateTimeProperty, value); }
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
Canvas = GetTemplateChild(PART_InternalCanvas) as CanvasControl;
|
||||
|
||||
Canvas.Draw += OnCanvasDraw;
|
||||
Canvas.PointerPressed += OnCanvasPointerPressed;
|
||||
|
||||
ForceDraw();
|
||||
}
|
||||
|
||||
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoDayTimelineCanvas control)
|
||||
{
|
||||
if (e.OldValue != null && e.NewValue == null)
|
||||
{
|
||||
control.RaiseCellUnselected();
|
||||
}
|
||||
|
||||
control.ForceDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseCellUnselected()
|
||||
{
|
||||
TimelineCellUnselected?.Invoke(this, new TimelineCellUnselectedArgs());
|
||||
}
|
||||
|
||||
private void OnCanvasPointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (RenderOptions == null) return;
|
||||
|
||||
var hourHeight = RenderOptions.CalendarSettings.HourHeight;
|
||||
|
||||
// When users click to cell we need to find the day, hour and minutes (first 30 minutes or second 30 minutes) that it represents on the timeline.
|
||||
|
||||
PointerPoint positionerRootPoint = e.GetCurrentPoint(PositionerUIElement);
|
||||
PointerPoint canvasPointerPoint = e.GetCurrentPoint(Canvas);
|
||||
|
||||
Point touchPoint = canvasPointerPoint.Position;
|
||||
|
||||
var singleDayWidth = (Canvas.ActualWidth / RenderOptions.TotalDayCount);
|
||||
|
||||
int day = (int)(touchPoint.X / singleDayWidth);
|
||||
int hour = (int)(touchPoint.Y / hourHeight);
|
||||
|
||||
bool isSecondHalf = touchPoint.Y % hourHeight > (hourHeight / 2);
|
||||
|
||||
var diffX = positionerRootPoint.Position.X - touchPoint.X;
|
||||
var diffY = positionerRootPoint.Position.Y - touchPoint.Y;
|
||||
|
||||
var cellStartRelativePositionX = diffX + (day * singleDayWidth);
|
||||
var cellEndRelativePositionX = cellStartRelativePositionX + singleDayWidth;
|
||||
|
||||
var cellStartRelativePositionY = diffY + (hour * hourHeight) + (isSecondHalf ? hourHeight / 2 : 0);
|
||||
var cellEndRelativePositionY = cellStartRelativePositionY + (isSecondHalf ? (hourHeight / 2) : hourHeight);
|
||||
|
||||
var cellSize = new Size(cellEndRelativePositionX - cellStartRelativePositionX, hourHeight / 2);
|
||||
var positionerPoint = new Point(cellStartRelativePositionX, cellStartRelativePositionY);
|
||||
|
||||
var clickedDateTime = RenderOptions.DateRange.StartDate.AddDays(day).AddHours(hour).AddMinutes(isSecondHalf ? 30 : 0);
|
||||
|
||||
// If there is already a selected date, in order to mimic the popup behavior, we need to dismiss the previous selection first.
|
||||
// Next click will be a new selection.
|
||||
|
||||
// Raise the events directly here instead of DP to not lose pointer position.
|
||||
if (clickedDateTime == SelectedDateTime || SelectedDateTime != null)
|
||||
{
|
||||
SelectedDateTime = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
TimelineCellSelected?.Invoke(this, new TimelineCellSelectedArgs(clickedDateTime, touchPoint, positionerPoint, cellSize));
|
||||
SelectedDateTime = clickedDateTime;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Clicked: {clickedDateTime}");
|
||||
}
|
||||
|
||||
public WinoDayTimelineCanvas()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoDayTimelineCanvas);
|
||||
}
|
||||
|
||||
private static void OnRenderingPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is WinoDayTimelineCanvas control)
|
||||
{
|
||||
control.ForceDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceDraw() => Canvas?.Invalidate();
|
||||
|
||||
private bool CanDrawTimeline()
|
||||
{
|
||||
return RenderOptions != null
|
||||
&& Canvas != null
|
||||
&& Canvas.ReadyToDraw
|
||||
&& WorkingHourCellBackgroundColor != null
|
||||
&& SeperatorColor != null
|
||||
&& HalfHourSeperatorColor != null
|
||||
&& SelectedCellBackgroundBrush != null;
|
||||
}
|
||||
|
||||
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
|
||||
{
|
||||
if (!CanDrawTimeline()) return;
|
||||
|
||||
int hours = 24;
|
||||
|
||||
double canvasWidth = Canvas.ActualWidth;
|
||||
double canvasHeight = Canvas.ActualHeight;
|
||||
|
||||
if (canvasWidth == 0 || canvasHeight == 0) return;
|
||||
|
||||
// Calculate the width of each rectangle (1 day column)
|
||||
// Equal distribution of the whole width.
|
||||
double rectWidth = canvasWidth / RenderOptions.TotalDayCount;
|
||||
|
||||
// Calculate the height of each rectangle (1 hour row)
|
||||
double rectHeight = RenderOptions.CalendarSettings.HourHeight;
|
||||
|
||||
// Define stroke and fill colors
|
||||
var strokeColor = SeperatorColor.Color;
|
||||
float strokeThickness = 0.5f;
|
||||
|
||||
for (int day = 0; day < RenderOptions.TotalDayCount; day++)
|
||||
{
|
||||
var currentDay = RenderOptions.DateRange.StartDate.AddDays(day);
|
||||
|
||||
bool isWorkingDay = RenderOptions.CalendarSettings.WorkingDays.Contains(currentDay.DayOfWeek);
|
||||
|
||||
// Loop through each hour (rows)
|
||||
for (int hour = 0; hour < hours; hour++)
|
||||
{
|
||||
var renderTime = TimeSpan.FromHours(hour);
|
||||
|
||||
var representingDateTime = currentDay.AddHours(hour);
|
||||
|
||||
// Calculate the position and size of the rectangle
|
||||
double x = day * rectWidth;
|
||||
double y = hour * rectHeight;
|
||||
|
||||
var rectangle = new Rect(x, y, rectWidth, rectHeight);
|
||||
|
||||
// Draw the rectangle border.
|
||||
// This is the main rectangle.
|
||||
args.DrawingSession.DrawRectangle(rectangle, strokeColor, strokeThickness);
|
||||
|
||||
// Fill another rectangle with the working hour background color
|
||||
// This rectangle must be placed with -1 margin to prevent invisible borders of the main rectangle.
|
||||
if (isWorkingDay && renderTime >= RenderOptions.CalendarSettings.WorkingHourStart && renderTime <= RenderOptions.CalendarSettings.WorkingHourEnd)
|
||||
{
|
||||
var backgroundRectangle = new Rect(x + 1, y + 1, rectWidth - 1, rectHeight - 1);
|
||||
|
||||
args.DrawingSession.DrawRectangle(backgroundRectangle, strokeColor, strokeThickness);
|
||||
args.DrawingSession.FillRectangle(backgroundRectangle, WorkingHourCellBackgroundColor.Color);
|
||||
}
|
||||
|
||||
// Draw a line in the center of the rectangle for representing half hours.
|
||||
double lineY = y + rectHeight / 2;
|
||||
|
||||
args.DrawingSession.DrawLine((float)x, (float)lineY, (float)(x + rectWidth), (float)lineY, HalfHourSeperatorColor.Color, strokeThickness, new CanvasStrokeStyle()
|
||||
{
|
||||
DashStyle = CanvasDashStyle.Dot
|
||||
});
|
||||
}
|
||||
|
||||
// Draw selected item background color for the date if possible.
|
||||
if (SelectedDateTime != null)
|
||||
{
|
||||
var selectedDateTime = SelectedDateTime.Value;
|
||||
if (selectedDateTime.Date == currentDay.Date)
|
||||
{
|
||||
var selectionRectHeight = rectHeight / 2;
|
||||
var selectedY = selectedDateTime.Hour * rectHeight + (selectedDateTime.Minute / 60) * rectHeight;
|
||||
|
||||
// Second half of the hour is selected.
|
||||
if (selectedDateTime.TimeOfDay.Minutes == 30)
|
||||
{
|
||||
selectedY += rectHeight / 2;
|
||||
}
|
||||
|
||||
var selectedRectangle = new Rect(day * rectWidth, selectedY, rectWidth, selectionRectHeight);
|
||||
args.DrawingSession.FillRectangle(selectedRectangle, SelectedCellBackgroundBrush.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Canvas == null) return;
|
||||
|
||||
Canvas.Draw -= OnCanvasDraw;
|
||||
Canvas.PointerPressed -= OnCanvasPointerPressed;
|
||||
Canvas.RemoveFromVisualTree();
|
||||
|
||||
Canvas = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,11 @@
|
||||
x:Class="Wino.Calendar.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Wino.Calendar"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Wino.Calendar"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
<Grid />
|
||||
</Page>
|
||||
|
||||
17
Wino.Calendar/Models/CalendarItemMeasurement.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Wino.Calendar.Models
|
||||
{
|
||||
public struct CalendarItemMeasurement
|
||||
{
|
||||
// Where to start?
|
||||
public double Left { get; set; }
|
||||
|
||||
// Extend until where?
|
||||
public double Right { get; set; }
|
||||
|
||||
public CalendarItemMeasurement(double left, double right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
<Identity
|
||||
Name="58272BurakKSE.WinoCalendar"
|
||||
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
|
||||
Version="1.0.0.0" />
|
||||
Version="1.0.7.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="f047b7dd-96ec-4d54-a862-9321e271e449" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
Square44x44Logo="Assets\Square44x44Logo.png"
|
||||
Description="Wino.Calendar"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square71x71Logo="Assets\SmallTile.png" Square310x310Logo="Assets\LargeTile.png"/>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
|
||||
@@ -21,11 +21,24 @@
|
||||
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||
the application package. The asterisks are not wildcards.
|
||||
-->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
|
||||
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
|
||||
<!-- Reduce memory footprint when building with Microsoft.Graph -->
|
||||
|
||||
<Assembly Name="Microsoft.Graph" Serialize="Excluded" />
|
||||
|
||||
<Assembly Name="Microsoft.Kiota.Abstractions" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Authentication.Azure" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Http.HttpClientLibrary" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Serialization.Form" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Serialization.Json" Dynamic="Public" />
|
||||
<Assembly Name="Microsoft.Kiota.Serialization.Multipart" Dynamic="Public" />
|
||||
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
<Type Name="Windows.Foundation.TypedEventHandler{Microsoft.UI.Xaml.Controls.NavigationView,Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs}" MarshalObject="Public" />
|
||||
|
||||
<Type Name="Microsoft.UI.Xaml.Controls.NavigationView">
|
||||
<Event Name="ItemInvoked" Dynamic="Required"/>
|
||||
</Type>
|
||||
</Application>
|
||||
</Directives>
|
||||
</Directives>
|
||||
|
||||
12
Wino.Calendar/Services/ApplicationResourceManager.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
{
|
||||
internal class ApplicationResourceManager
|
||||
{
|
||||
}
|
||||
}
|
||||
15
Wino.Calendar/Services/DialogService.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.UWP.Services;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
{
|
||||
public class DialogService : DialogServiceBase, ICalendarDialogService
|
||||
{
|
||||
public DialogService(IThemeService themeService,
|
||||
IConfigurationService configurationService,
|
||||
IApplicationResourceManager<ResourceDictionary> applicationResourceManager) : base(themeService, configurationService, applicationResourceManager)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Wino.Calendar/Services/NavigationService.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Calendar.Views;
|
||||
using Wino.Calendar.Views.Account;
|
||||
using Wino.Calendar.Views.Settings;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.UWP.Services;
|
||||
using Wino.Views;
|
||||
|
||||
namespace Wino.Calendar.Services
|
||||
{
|
||||
public class NavigationService : NavigationServiceBase, INavigationService
|
||||
{
|
||||
public Type GetPageType(WinoPage winoPage)
|
||||
{
|
||||
switch (winoPage)
|
||||
{
|
||||
case WinoPage.CalendarPage:
|
||||
return typeof(CalendarPage);
|
||||
case WinoPage.SettingsPage:
|
||||
return typeof(SettingsPage);
|
||||
case WinoPage.CalendarSettingsPage:
|
||||
return typeof(CalendarSettingsPage);
|
||||
case WinoPage.AccountManagementPage:
|
||||
return typeof(AccountManagementPage);
|
||||
default:
|
||||
throw new Exception("Page is not implemented yet.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool Navigate(WinoPage page, object parameter = null, NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, NavigationTransitionType transition = NavigationTransitionType.None)
|
||||
{
|
||||
// All navigations are performed on shell frame for calendar.
|
||||
|
||||
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
|
||||
{
|
||||
var shellFrame = shellPage.GetShellFrame();
|
||||
|
||||
var pageType = GetPageType(page);
|
||||
|
||||
shellFrame.Navigate(pageType, parameter);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Wino.Calendar/Services/SettingsBuilderService.cs
Normal file
21
Wino.Calendar/Styles/CalendarDayItemsControl.xaml
Normal file
@@ -0,0 +1,21 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls">
|
||||
|
||||
<!-- Calendar day items. -->
|
||||
<Style TargetType="controls:CalendarDayItemsControl">
|
||||
<Style.Setters>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:CalendarDayItemsControl">
|
||||
<controls:WinoCalendarPanel
|
||||
x:Name="PART_CalendarPanel"
|
||||
DayModel="{TemplateBinding DayModel}"
|
||||
EventItemMargin="0" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
29
Wino.Calendar/Styles/CalendarItemControlResources.xaml
Normal file
@@ -0,0 +1,29 @@
|
||||
<ResourceDictionary
|
||||
x:Class="Wino.Calendar.Styles.CalendarItemControlResources"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls">
|
||||
|
||||
<!-- CalendarItemControl -->
|
||||
<Style TargetType="controls:CalendarItemControl">
|
||||
<Style.Setters>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:CalendarItemControl">
|
||||
<Grid
|
||||
Margin="1,1,1,1"
|
||||
Background="#cf2b36"
|
||||
BorderBrush="Gray"
|
||||
CornerRadius="3">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Item.Name, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
12
Wino.Calendar/Styles/CalendarItemControlResources.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Calendar.Styles
|
||||
{
|
||||
public sealed partial class CalendarItemControlResources : ResourceDictionary
|
||||
{
|
||||
public CalendarItemControlResources()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Wino.Calendar/Styles/CalendarRenderStyles.xaml
Normal file
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<StaticResource x:Key="WinoCalendarViewTodayBackgroundBrush" ResourceKey="SystemControlBackgroundAccentBrush" />
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="CalendarSeperatorBrush">#D9D9D9</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarFieldWorkingHoursBackgroundBrush">#ffffff</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarFieldWorkingNonHoursBackgroundBrush">#f9f9f9</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarFieldSelectedBackgroundBrush">#ebebeb</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="WinoCalendarViewBorderBrush">Yellow</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="WinoCalendarViewVisibleDayBackgroundBrush">#dfe4ea</SolidColorBrush>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="CalendarSeperatorBrush">#000000</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarFieldWorkingHoursBackgroundBrush">#262626</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarFieldWorkingNonHoursBackgroundBrush">#1a1a1a</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarFieldSelectedBackgroundBrush">#121212</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="WinoCalendarViewBorderBrush">#3d3d3d</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="WinoCalendarViewVisibleDayBackgroundBrush">#2f3542</SolidColorBrush>
|
||||
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
93
Wino.Calendar/Styles/DayColumnControl.xaml
Normal file
@@ -0,0 +1,93 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls">
|
||||
|
||||
<!-- Top column header DayColumnControl -->
|
||||
<Style TargetType="controls:DayColumnControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:DayColumnControl">
|
||||
<Grid MinHeight="100" MaxHeight="150">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="25" />
|
||||
<RowDefinition Height="7" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Name of the day. Monday, Tuesday etc. at the top. -->
|
||||
<TextBlock
|
||||
x:Name="PART_ColumnHeaderText"
|
||||
Margin="8,0,0,0"
|
||||
FontSize="16"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Grid.RowSpan="2"
|
||||
BorderBrush="{ThemeResource CalendarSeperatorBrush}"
|
||||
BorderThickness="1,1,0,1" />
|
||||
|
||||
<!-- Border for today indication. -->
|
||||
<Border
|
||||
x:Name="PART_IsTodayBorder"
|
||||
Grid.Row="1"
|
||||
Height="5"
|
||||
Margin="2,0,2,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource SystemAccentColor}"
|
||||
CornerRadius="2"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<!-- Place where full day events go. -->
|
||||
<Grid
|
||||
x:Name="PART_DayDataAreaGrid"
|
||||
Grid.Row="2"
|
||||
Padding="6"
|
||||
BorderBrush="{ThemeResource CalendarSeperatorBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Day number -->
|
||||
<TextBlock x:Name="PART_HeaderDateDayText" FontSize="17" />
|
||||
|
||||
<!-- Extras -->
|
||||
<StackPanel Grid.Column="1" HorizontalAlignment="Right" />
|
||||
|
||||
<!-- Events -->
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
VerticalScrollBarVisibility="Hidden" />
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="TodayOrNotStates">
|
||||
<VisualState x:Name="NotTodayState" />
|
||||
<VisualState x:Name="TodayState">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="PART_IsTodayBorder.Visibility" Value="Visible" />
|
||||
<Setter Target="PART_HeaderDateDayText.Foreground" Value="{ThemeResource SystemAccentColor}" />
|
||||
<Setter Target="PART_HeaderDateDayText.FontWeight" Value="Semibold" />
|
||||
<Setter Target="PART_ColumnHeaderText.FontWeight" Value="Semibold" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
|
||||
</ResourceDictionary>
|
||||
23
Wino.Calendar/Styles/DayHeaderControl.xaml
Normal file
@@ -0,0 +1,23 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls">
|
||||
|
||||
<!-- Left day header DayHeaderControl -->
|
||||
<Style TargetType="controls:DayHeaderControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:DayHeaderControl">
|
||||
<Grid>
|
||||
<TextBlock
|
||||
x:Name="PART_DayHeaderTextBlock"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="12" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
128
Wino.Calendar/Styles/WinoCalendarResources.xaml
Normal file
@@ -0,0 +1,128 @@
|
||||
<ResourceDictionary
|
||||
x:Class="Wino.Calendar.Styles.WinoCalendarResources"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:WinoCalendarControl.Styles"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:Wino.Core.Domain.Models.Calendar"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:toolkitControls="using:CommunityToolkit.WinUI.Controls">
|
||||
|
||||
<!-- 08:00 or 8 AM/PM on the left etc. -->
|
||||
<DataTemplate x:Key="DayCalendarHourHeaderTemplate" x:DataType="models:DayHeaderRenderModel">
|
||||
<Grid Height="{x:Bind HourHeight}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Text="{x:Bind DayHeader}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Vertical panel that renders items on canvas. -->
|
||||
<DataTemplate x:Key="DayCalendarItemVerticalRenderTemplate" x:DataType="models:CalendarDayModel">
|
||||
<controls:CalendarDayItemsControl DayModel="{x:Bind}" />
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Equally distributed days of week representation in FlipView. -->
|
||||
<DataTemplate x:Key="FlipTemplate" x:DataType="models:DayRangeRenderModel">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
ColumnSpacing="0"
|
||||
RowSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ItemsControl
|
||||
Grid.Column="1"
|
||||
Margin="50,0,16,0"
|
||||
ItemsSource="{x:Bind CalendarDays}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:CalendarDayModel">
|
||||
<controls:DayColumnControl DayModel="{x:Bind}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<toolkitControls:UniformGrid
|
||||
Columns="{Binding CalendarRenderOptions.TotalDayCount}"
|
||||
Orientation="Horizontal"
|
||||
Rows="1" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0"
|
||||
Padding="0,0,16,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Rendering left hour headers. -->
|
||||
<ItemsControl ItemTemplate="{StaticResource DayCalendarHourHeaderTemplate}" ItemsSource="{x:Bind DayHeaders}" />
|
||||
|
||||
<!-- Drawing canvas for timeline. -->
|
||||
<controls:WinoDayTimelineCanvas
|
||||
Grid.Column="1"
|
||||
HalfHourSeperatorColor="{ThemeResource CalendarSeperatorBrush}"
|
||||
PositionerUIElement="{Binding ElementName=RootGrid}"
|
||||
RenderOptions="{x:Bind CalendarRenderOptions}"
|
||||
SelectedCellBackgroundBrush="{ThemeResource CalendarFieldSelectedBackgroundBrush}"
|
||||
SeperatorColor="{ThemeResource CalendarSeperatorBrush}"
|
||||
WorkingHourCellBackgroundColor="{ThemeResource CalendarFieldWorkingHoursBackgroundBrush}" />
|
||||
|
||||
<!-- Each vertical day grids that renders events. -->
|
||||
<ItemsControl
|
||||
Grid.Column="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource DayCalendarItemVerticalRenderTemplate}"
|
||||
ItemsSource="{x:Bind CalendarDays}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<toolkitControls:UniformGrid
|
||||
Columns="{Binding CalendarRenderOptions.TotalDayCount}"
|
||||
Orientation="Horizontal"
|
||||
Rows="1" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Default style for WinoCalendarControl -->
|
||||
<Style TargetType="controls:WinoCalendarControl">
|
||||
<Style.Setters>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:WinoCalendarControl">
|
||||
<controls:WinoCalendarFlipView
|
||||
x:Name="PART_WinoFlipView"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FlipTemplate}"
|
||||
ItemsSource="{TemplateBinding DayRanges}"
|
||||
SelectedIndex="{Binding SelectedFlipViewIndex, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<!-- Teaching tip dialog sizing. -->
|
||||
|
||||
<x:Double x:Key="TeachingTipMinWidth">1000</x:Double>
|
||||
<x:Double x:Key="TeachingTipMaxWidth">1000</x:Double>
|
||||
|
||||
</ResourceDictionary>
|
||||
12
Wino.Calendar/Styles/WinoCalendarResources.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Calendar.Styles
|
||||
{
|
||||
public sealed partial class WinoCalendarResources : ResourceDictionary
|
||||
{
|
||||
public WinoCalendarResources()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Wino.Calendar/Styles/WinoCalendarTypeSelectorControl.xaml
Normal file
46
Wino.Calendar/Styles/WinoCalendarView.xaml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CalendarRenderStyles.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="controls:WinoCalendarView">
|
||||
<Setter Property="VisibleDateBackground" Value="{ThemeResource WinoCalendarViewVisibleDayBackgroundBrush}" />
|
||||
<Setter Property="TodayBackgroundBrush" Value="{ThemeResource SystemColorControlAccentBrush}" />
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:WinoCalendarView">
|
||||
<CalendarView
|
||||
x:Name="PART_CalendarView"
|
||||
BorderBrush="{ThemeResource WinoCalendarViewBorderBrush}"
|
||||
CalendarItemCornerRadius="5"
|
||||
CornerRadius="4"
|
||||
DayItemMargin="0"
|
||||
IsTodayHighlighted="False"
|
||||
SelectionMode="Single">
|
||||
<CalendarView.CalendarViewDayItemStyle>
|
||||
<Style TargetType="CalendarViewDayItem">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="CalendarViewDayItem">
|
||||
<Grid>
|
||||
<Border x:Name="PART_DayViewItemBorder" CornerRadius="5">
|
||||
<ContentPresenter />
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</CalendarView.CalendarViewDayItemStyle>
|
||||
</CalendarView>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
19
Wino.Calendar/Styles/WinoDayTimelineCanvas.xaml
Normal file
@@ -0,0 +1,19 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||
xmlns:controls="using:Wino.Calendar.Controls">
|
||||
|
||||
<!-- Background Timeline Canvas -->
|
||||
<Style TargetType="controls:WinoDayTimelineCanvas">
|
||||
<Style.Setters>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:WinoDayTimelineCanvas">
|
||||
<canvas:CanvasControl x:Name="PART_InternalCanvas" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,7 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public class AccountManagementPageAbstract : BasePage<AccountManagementViewModel> { }
|
||||
}
|
||||
7
Wino.Calendar/Views/Abstract/AppShellAbstract.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class AppShellAbstract : BasePage<AppShellViewModel> { }
|
||||
}
|
||||
7
Wino.Calendar/Views/Abstract/CalendarPageAbstract.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class CalendarPageAbstract : BasePage<CalendarPageViewModel> { }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Wino.Calendar.ViewModels;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Calendar.Views.Abstract
|
||||
{
|
||||
public abstract class CalendarSettingsPageAbstract : BasePage<CalendarSettingsPageViewModel> { }
|
||||
}
|
||||
12
Wino.Calendar/Views/Account/AccountManagementPage.xaml
Normal file
@@ -0,0 +1,12 @@
|
||||
<abstract:AccountManagementPageAbstract
|
||||
x:Class="Wino.Calendar.Views.Account.AccountManagementPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Wino.Calendar.Views.Account"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid />
|
||||
</abstract:AccountManagementPageAbstract>
|
||||
12
Wino.Calendar/Views/Account/AccountManagementPage.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
|
||||
namespace Wino.Calendar.Views.Account
|
||||
{
|
||||
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
|
||||
{
|
||||
public AccountManagementPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
251
Wino.Calendar/Views/AppShell.xaml
Normal file
@@ -0,0 +1,251 @@
|
||||
<abstract:AppShellAbstract
|
||||
x:Class="Wino.Calendar.Views.AppShell"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
|
||||
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
|
||||
xmlns:calendarControls="using:Wino.Calendar.Controls"
|
||||
xmlns:communityControls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:coreControls="using:Wino.Core.UWP.Controls"
|
||||
xmlns:coreSelectors="using:Wino.Core.UWP.Selectors"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Wino.Calendar.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
muxc:BackdropMaterial.ApplyToRootOrPageBackground="{ThemeResource UseMica}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<coreSelectors:NavigationMenuTemplateSelector
|
||||
x:Key="NavigationMenuTemplateSelector"
|
||||
AccountManagementTemplate="{StaticResource ManageAccountsTemplate}"
|
||||
RatingItemTemplate="{StaticResource RatingItemTemplate}"
|
||||
SeperatorTemplate="{StaticResource SeperatorTemplate}"
|
||||
SettingsItemTemplate="{StaticResource SettingsItemTemplate}" />
|
||||
|
||||
<Style
|
||||
x:Key="CalendarNavigationButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Margin" Value="0,4,0,0" />
|
||||
<Setter Property="Padding" Value="8,4,8,6" />
|
||||
<Setter Property="CornerRadius" Value="6" />
|
||||
<Setter Property="Width" Value="40" />
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Padding="0"
|
||||
ColumnSpacing="0"
|
||||
RowSpacing="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="48" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- CoreWindowText="{x:Bind ViewModel.StatePersistenceService.CoreWindowTitle, Mode=OneWay}" -->
|
||||
<coreControls:WinoAppTitleBar
|
||||
x:Name="RealAppBar"
|
||||
Grid.ColumnSpan="2"
|
||||
BackButtonClicked="BackButtonClicked"
|
||||
Canvas.ZIndex="150"
|
||||
ConnectionStatus="{x:Bind ViewModel.ActiveConnectionStatus, Mode=OneWay}"
|
||||
CoreWindowText="Wino Calendar"
|
||||
IsBackButtonVisible="{x:Bind ViewModel.StatePersistenceService.IsBackButtonVisible, Mode=OneWay}"
|
||||
IsNavigationPaneOpen="{x:Bind MainNavigationView.IsPaneOpen, Mode=TwoWay}"
|
||||
NavigationViewDisplayMode="{x:Bind MainNavigationView.DisplayMode, Mode=OneWay}"
|
||||
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=OneWay}"
|
||||
ReconnectCommand="{x:Bind ViewModel.ReconnectServerCommand}"
|
||||
ShrinkShellContentOnExpansion="False"
|
||||
SystemReserved="180">
|
||||
<coreControls:WinoAppTitleBar.ShellFrameContent>
|
||||
<Grid Margin="4,0,0,0" ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid
|
||||
x:Name="DragArea"
|
||||
Grid.ColumnSpan="3"
|
||||
Background="Transparent" />
|
||||
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="3*" />
|
||||
<ColumnDefinition Width="7*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<AutoSuggestBox
|
||||
x:Name="SearchBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="Transparent"
|
||||
PlaceholderText="Search" />
|
||||
|
||||
<StackPanel
|
||||
x:Name="NavigationTitleStack"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,12,4"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<Button
|
||||
x:Name="PreviousDateButton"
|
||||
Click="PreviousDateClicked"
|
||||
Style="{StaticResource CalendarNavigationButtonStyle}">
|
||||
<PathIcon x:Name="PreviousDateButtonPathIcon" Data="F1 M 8.72 18.599998 C 8.879999 18.733334 9.059999 18.799999 9.26 18.799999 C 9.459999 18.799999 9.633332 18.719999 9.78 18.559999 C 9.926666 18.4 10 18.219999 10 18.019999 C 10 17.82 9.92 17.653332 9.76 17.52 L 4.52 12.559999 L 17.24 12.559999 C 17.453333 12.559999 17.633331 12.486667 17.779999 12.339999 C 17.926666 12.193334 18 12.013333 18 11.799999 C 18 11.586666 17.926666 11.406667 17.779999 11.259999 C 17.633331 11.113333 17.453333 11.039999 17.24 11.039999 L 4.52 11.039999 L 9.76 6.08 C 9.973333 5.893333 10.046666 5.653332 9.98 5.359999 C 9.913333 5.066666 9.74 4.880001 9.46 4.799999 C 9.179999 4.720001 8.933332 4.786667 8.72 5 L 2.32 11.08 C 2.16 11.24 2.053333 11.426666 2 11.639999 C 1.973333 11.746666 1.973333 11.853333 2 11.959999 C 2.053333 12.173333 2.16 12.360001 2.32 12.52 Z " />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
x:Name="NextDateButton"
|
||||
Click="NextDateClicked"
|
||||
Style="{StaticResource CalendarNavigationButtonStyle}">
|
||||
<PathIcon x:Name="NextDateButtonPathIcon" Data="F1 M 11.28 5 C 11.12 4.866667 10.94 4.806667 10.74 4.82 C 10.539999 4.833334 10.366666 4.913334 10.219999 5.059999 C 10.073333 5.206665 10 5.379999 10 5.58 C 10 5.779999 10.08 5.946667 10.24 6.08 L 15.48 11.039999 L 2.76 11.039999 C 2.546667 11.039999 2.366667 11.113333 2.22 11.259999 C 2.073333 11.406667 2 11.586666 2 11.799999 C 2 12.013333 2.073333 12.193334 2.22 12.339999 C 2.366667 12.486667 2.546667 12.559999 2.76 12.559999 L 15.48 12.559999 L 10.24 17.52 C 10.026667 17.706665 9.953333 17.946667 10.02 18.24 C 10.086666 18.533333 10.259999 18.719999 10.54 18.799999 C 10.82 18.879999 11.066667 18.813334 11.28 18.599998 L 17.68 12.52 C 17.84 12.360001 17.946667 12.173333 18 11.959999 C 18 11.853333 18 11.746666 18 11.639999 C 17.946667 11.426666 17.84 11.24 17.68 11.08 Z " />
|
||||
</Button>
|
||||
|
||||
<calendarControls:CustomCalendarFlipView
|
||||
x:Name="DayHeaderNavigationItemsFlipView"
|
||||
MaxHeight="30"
|
||||
Margin="8,4,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsHitTestVisible="False"
|
||||
ItemsSource="{x:Bind ViewModel.DateNavigationHeaderItems}"
|
||||
SelectedIndex="{x:Bind ViewModel.SelectedDateNavigationHeaderIndex, Mode=OneWay}">
|
||||
<FlipView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="18"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding}" />
|
||||
</DataTemplate>
|
||||
</FlipView.ItemTemplate>
|
||||
</calendarControls:CustomCalendarFlipView>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<calendarControls:WinoCalendarTypeSelectorControl
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
DisplayDayCount="{x:Bind ViewModel.StatePersistenceService.DayDisplayCount, Mode=OneWay}"
|
||||
SelectedType="{x:Bind ViewModel.StatePersistenceService.CalendarDisplayType, Mode=TwoWay}"
|
||||
TodayClickedCommand="{x:Bind ViewModel.TodayClickedCommand}" />
|
||||
</Grid>
|
||||
</coreControls:WinoAppTitleBar.ShellFrameContent>
|
||||
</coreControls:WinoAppTitleBar>
|
||||
|
||||
<Grid
|
||||
Grid.RowSpan="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Background="{ThemeResource WinoApplicationBackgroundColor}"
|
||||
IsHitTestVisible="False" />
|
||||
|
||||
<muxc:NavigationView
|
||||
x:Name="MainNavigationView"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="3"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
AlwaysShowHeader="True"
|
||||
FooterMenuItemsSource="{x:Bind ViewModel.FooterItems, Mode=OneWay}"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
IsPaneOpen="{x:Bind ViewModel.PreferencesService.IsNavigationPaneOpened, Mode=TwoWay}"
|
||||
IsPaneToggleButtonVisible="False"
|
||||
IsSettingsVisible="False"
|
||||
IsTabStop="True"
|
||||
IsTitleBarAutoPaddingEnabled="False"
|
||||
MenuItemTemplateSelector="{StaticResource NavigationMenuTemplateSelector}"
|
||||
MenuItemsSource="{x:Bind ViewModel.MenuItems, Mode=OneWay}"
|
||||
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=TwoWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedMenuItem, Mode=TwoWay}">
|
||||
<muxc:NavigationView.ContentTransitions>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition />
|
||||
</TransitionCollection>
|
||||
</muxc:NavigationView.ContentTransitions>
|
||||
|
||||
<muxc:NavigationView.PaneHeader>
|
||||
<calendarControls:WinoCalendarView
|
||||
x:Name="CalendarView"
|
||||
Margin="2,0"
|
||||
HorizontalAlignment="Center"
|
||||
DateClickedCommand="{x:Bind ViewModel.DateClickedCommand}"
|
||||
HighlightedDateRange="{x:Bind ViewModel.HighlightedDateRange, Mode=OneWay}" />
|
||||
</muxc:NavigationView.PaneHeader>
|
||||
<muxc:NavigationView.Content>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Main Content -->
|
||||
<Frame
|
||||
x:Name="ShellFrame"
|
||||
Padding="0,0,7,7"
|
||||
IsNavigationStackEnabled="False"
|
||||
Navigated="ShellFrameContentNavigated">
|
||||
<Frame.ContentTransitions>
|
||||
<TransitionCollection>
|
||||
<PopupThemeTransition />
|
||||
</TransitionCollection>
|
||||
</Frame.ContentTransitions>
|
||||
</Frame>
|
||||
|
||||
<!-- InfoBar -->
|
||||
<coreControls:WinoInfoBar
|
||||
x:Name="ShellInfoBar"
|
||||
MaxWidth="700"
|
||||
Margin="0,60,25,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsOpen="False" />
|
||||
</Grid>
|
||||
</muxc:NavigationView.Content>
|
||||
</muxc:NavigationView>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="LowResolutionStates">
|
||||
<VisualState x:Name="BigScreen">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="1200" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SmallScreen">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="NavigationTitleStack.Visibility" Value="Collapsed" />
|
||||
<Setter Target="SearchBox.(Grid.ColumnSpan)" Value="2" />
|
||||
</VisualState.Setters>
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
</VisualState.StateTriggers>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="CalendarOrientationStates">
|
||||
<VisualState x:Name="HorizontalCalendar" />
|
||||
<VisualState x:Name="VerticalCalendar">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DayHeaderNavigationItemsFlipView.ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="PreviousDateButtonPathIcon.Data" Value="F1 M 16.799999 13.079999 C 16.933332 12.92 16.993332 12.74 16.98 12.539999 C 16.966665 12.34 16.886665 12.166667 16.74 12.02 C 16.593334 11.873333 16.42 11.799999 16.219999 11.799999 C 16.02 11.799999 15.853333 11.879999 15.719999 12.039999 L 10.76 17.279999 L 10.76 4.559999 C 10.76 4.346666 10.686666 4.166668 10.54 4.02 C 10.393332 3.873333 10.213333 3.799999 10 3.799999 C 9.786666 3.799999 9.606666 3.873333 9.46 4.02 C 9.313333 4.166668 9.24 4.346666 9.24 4.559999 L 9.24 17.279999 L 4.28 12.039999 C 4.146667 11.879999 3.98 11.799999 3.78 11.799999 C 3.58 11.799999 3.4 11.873333 3.24 12.02 C 3.08 12.166667 3 12.34 3 12.539999 C 3 12.74 3.066667 12.92 3.2 13.079999 L 9.28 19.48 C 9.439999 19.639999 9.626666 19.746666 9.84 19.799999 C 9.946667 19.799999 10.053333 19.799999 10.16 19.799999 C 10.373333 19.746666 10.559999 19.639999 10.719999 19.48 Z " />
|
||||
<Setter Target="NextDateButtonPathIcon.Data" Value="F1 M 3.2 10.52 C 2.986666 10.733333 2.92 10.98 3 11.259999 C 3.08 11.54 3.266666 11.713333 3.56 11.78 C 3.853333 11.846666 4.093333 11.773333 4.28 11.559999 L 9.24 6.32 L 9.24 19.039999 C 9.24 19.253332 9.313333 19.433332 9.46 19.58 C 9.606666 19.726665 9.786666 19.799999 10 19.799999 C 10.213333 19.799999 10.393332 19.726665 10.54 19.58 C 10.686666 19.433332 10.76 19.253332 10.76 19.039999 L 10.76 6.32 L 15.719999 11.559999 C 15.906666 11.773333 16.139999 11.846666 16.42 11.78 C 16.700001 11.713333 16.886665 11.54 16.98 11.259999 C 17.073332 10.98 17.013332 10.733333 16.799999 10.52 L 10.719999 4.119999 C 10.559999 3.959999 10.373333 3.853333 10.16 3.799999 C 10.053333 3.799999 9.946667 3.799999 9.84 3.799999 C 9.626666 3.853333 9.439999 3.959999 9.28 4.119999 Z " />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</abstract:AppShellAbstract>
|
||||
57
Wino.Calendar/Views/AppShell.xaml.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Calendar.Views.Abstract;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Views
|
||||
{
|
||||
public sealed partial class AppShell : AppShellAbstract,
|
||||
IRecipient<GoToCalendarDayMessage>
|
||||
{
|
||||
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
|
||||
private const string STATE_VerticalCalendar = "VerticalCalendar";
|
||||
|
||||
public Frame GetShellFrame() => ShellFrame;
|
||||
public AppShell()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Window.Current.SetTitleBar(DragArea);
|
||||
|
||||
ViewModel.DisplayTypeChanged += CalendarDisplayTypeChanged;
|
||||
}
|
||||
|
||||
private void CalendarDisplayTypeChanged(object sender, Core.Domain.Enums.CalendarDisplayType e)
|
||||
{
|
||||
// Go to different states based on the display type.
|
||||
if (ViewModel.IsVerticalCalendar)
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_HorizontalCalendar, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void BackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, Windows.UI.Xaml.RoutedEventArgs args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Receive(GoToCalendarDayMessage message)
|
||||
{
|
||||
CalendarView.GoToDay(message.DateTime);
|
||||
}
|
||||
|
||||
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());
|
||||
|
||||
private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage());
|
||||
}
|
||||
}
|
||||
75
Wino.Calendar/Views/CalendarPage.xaml
Normal file
@@ -0,0 +1,75 @@
|
||||
<abstract:CalendarPageAbstract
|
||||
x:Class="Wino.Calendar.Views.CalendarPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
|
||||
xmlns:calendarControls="using:Wino.Calendar.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Wino.Calendar.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
<!--<Page.Resources>
|
||||
<x:Double x:Key="TeachingTipMinWidth">500</x:Double>
|
||||
<x:Double x:Key="TeachingTipMaxWidth">500</x:Double>
|
||||
</Page.Resources>-->
|
||||
<Border
|
||||
Margin="0,0,7,7"
|
||||
Background="{ThemeResource WinoContentZoneBackgroud}"
|
||||
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="7">
|
||||
<Grid>
|
||||
<calendarControls:WinoCalendarControl
|
||||
x:Name="CalendarControl"
|
||||
DayRanges="{x:Bind ViewModel.DayRanges}"
|
||||
IsHitTestVisible="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
|
||||
SelectedFlipViewDayRange="{x:Bind ViewModel.SelectedDayRange, Mode=TwoWay}"
|
||||
SelectedFlipViewIndex="{x:Bind ViewModel.SelectedDateRangeIndex, Mode=TwoWay}"
|
||||
TimelineCellSelected="CellSelected"
|
||||
TimelineCellUnselected="CellUnselected" />
|
||||
|
||||
<Canvas x:Name="CalendarOverlayCanvas" IsHitTestVisible="False">
|
||||
<!-- Invisible target UI element for teaching tip display. -->
|
||||
<Grid
|
||||
x:Name="TeachingTipPositionerGrid"
|
||||
Background="Transparent"
|
||||
Visibility="Visible" />
|
||||
|
||||
<!-- Single teaching tip to display create event dialog. -->
|
||||
<muxc:TeachingTip
|
||||
x:Name="NewEventTip"
|
||||
Width="500"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
Closed="CreateEventTipClosed"
|
||||
IsOpen="False"
|
||||
PreferredPlacement="Right"
|
||||
Target="{x:Bind TeachingTipPositionerGrid}">
|
||||
<muxc:TeachingTip.Content>
|
||||
<Grid Margin="0,24" RowSpacing="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TimePicker x:Name="EventTimePicker" ClockIdentifier="24HourClock" />
|
||||
|
||||
|
||||
<!-- Create events dialog -->
|
||||
<Button
|
||||
x:Name="AddEvent"
|
||||
Grid.Row="1"
|
||||
Click="AddEventClicked"
|
||||
Content="Add Event" />
|
||||
|
||||
</Grid>
|
||||
</muxc:TeachingTip.Content>
|
||||
</muxc:TeachingTip>
|
||||
</Canvas>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</abstract:CalendarPageAbstract>
|
||||
|
||||