Skip to content

Commit 96788b1

Browse files
authored
Add ability to specify a selection direction for the calendar (#189)
For example "StartToEnd" where after selecting a date, only later dates can be selected.
2 parents 64b25df + c9b2658 commit 96788b1

File tree

9 files changed

+191
-6
lines changed

9 files changed

+191
-6
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace XCalendar.Core.Enums
2+
{
3+
public enum SelectionDirection
4+
{
5+
Any,
6+
StartToEnd,
7+
EndToStart,
8+
Confined,
9+
ConfinedReverse,
10+
}
11+
}

XCalendar.Core/Interfaces/ICalendar.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ namespace XCalendar.Core.Interfaces
3939
DateTime? RangeSelectionStart { get; set; }
4040
DateTime? RangeSelectionEnd { get; set; }
4141
SelectionType SelectionType { get; set; }
42+
SelectionDirection SelectionDirection { get; set; }
4243
#endregion
4344

4445
#region Events

XCalendar.Core/Models/Calendar.cs

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public class Calendar : Calendar<CalendarDay, Event>
5454
private DateTime? _rangeSelectionStart;
5555
private DateTime? _rangeSelectionEnd;
5656
private SelectionType _selectionType = SelectionType.None;
57+
private SelectionDirection _selectionDirection = SelectionDirection.Any;
5758
private ObservableRangeCollection<TEvent> _events = new ObservableRangeCollection<TEvent>();
5859
#endregion
5960

@@ -447,6 +448,25 @@ public SelectionType SelectionType
447448
}
448449
}
449450
}
451+
/// <summary>
452+
/// The direction a selected date has to be relative to the earliest or latest selected date in order to be applied to the selection.
453+
/// </summary>
454+
public SelectionDirection SelectionDirection
455+
{
456+
get
457+
{
458+
return _selectionDirection;
459+
}
460+
set
461+
{
462+
if (_selectionDirection != value)
463+
{
464+
_selectionDirection = value;
465+
466+
OnPropertyChanged(nameof(SelectionDirection));
467+
}
468+
}
469+
}
450470
public ObservableRangeCollection<TEvent> Events
451471
{
452472
get
@@ -537,6 +557,9 @@ protected virtual void OnDaysUpdating()
537557
/// <param name="dateTime">The <see cref="DateTime"/> to select/unselect.</param>
538558
public virtual void ChangeDateSelection(DateTime dateTime)
539559
{
560+
DateTime? startSelectedDate = SelectedDates.Any() ? SelectedDates.Min() : (DateTime?)null;
561+
DateTime? endSelectedDate = SelectedDates.Skip(1).Any() ? SelectedDates.Max() : (DateTime?)null;
562+
540563
switch (SelectionType)
541564
{
542565
case SelectionType.None:
@@ -546,11 +569,41 @@ public virtual void ChangeDateSelection(DateTime dateTime)
546569
switch (SelectionAction)
547570
{
548571
case SelectionAction.Add:
549-
if (!SelectedDates.Any(x => x.Date == dateTime.Date))
550572
{
551-
SelectedDates.Add(dateTime.Date);
573+
bool isCorrectDirection;
574+
575+
switch (SelectionDirection)
576+
{
577+
case SelectionDirection.Any:
578+
isCorrectDirection = true;
579+
break;
580+
581+
case SelectionDirection.StartToEnd:
582+
isCorrectDirection = startSelectedDate == null || dateTime.Date >= startSelectedDate.Value.Date;
583+
break;
584+
585+
case SelectionDirection.EndToStart:
586+
isCorrectDirection = endSelectedDate == null || dateTime.Date <= endSelectedDate.Value.Date;
587+
break;
588+
589+
case SelectionDirection.Confined:
590+
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || (dateTime.Date >= startSelectedDate.Value.Date && dateTime.Date <= endSelectedDate.Value.Date);
591+
break;
592+
593+
case SelectionDirection.ConfinedReverse:
594+
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || dateTime.Date <= startSelectedDate.Value.Date || dateTime.Date >= endSelectedDate.Value.Date;
595+
break;
596+
597+
default:
598+
throw new NotImplementedException();
599+
}
600+
601+
if (isCorrectDirection && !SelectedDates.Any(x => x.Date == dateTime.Date))
602+
{
603+
SelectedDates.Add(dateTime.Date);
604+
}
605+
break;
552606
}
553-
break;
554607

555608
case SelectionAction.Remove:
556609
if (SelectedDates.Any(x => x.Date == dateTime.Date))
@@ -566,12 +619,43 @@ public virtual void ChangeDateSelection(DateTime dateTime)
566619
}
567620
else
568621
{
569-
SelectedDates.Add(dateTime.Date);
622+
bool isCorrectDirection;
623+
624+
switch (SelectionDirection)
625+
{
626+
case SelectionDirection.Any:
627+
isCorrectDirection = true;
628+
break;
629+
630+
case SelectionDirection.StartToEnd:
631+
isCorrectDirection = startSelectedDate == null || dateTime.Date >= startSelectedDate.Value.Date;
632+
break;
633+
634+
case SelectionDirection.EndToStart:
635+
isCorrectDirection = endSelectedDate == null || dateTime.Date <= endSelectedDate.Value.Date;
636+
break;
637+
638+
case SelectionDirection.Confined:
639+
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || (dateTime.Date >= startSelectedDate.Value.Date && dateTime.Date <= endSelectedDate.Value.Date);
640+
break;
641+
642+
case SelectionDirection.ConfinedReverse:
643+
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || dateTime.Date <= startSelectedDate.Value.Date || dateTime.Date >= endSelectedDate.Value.Date;
644+
break;
645+
646+
default:
647+
throw new NotImplementedException();
648+
}
649+
650+
if (isCorrectDirection)
651+
{
652+
SelectedDates.Add(dateTime.Date);
653+
}
570654
}
571655
break;
572656

573657
case SelectionAction.Replace:
574-
if (SelectedDates.Count != 1 || (SelectedDates.Count == 1 && SelectedDates.First().Date != dateTime.Date))
658+
if (SelectedDates.Count != 1 || (SelectedDates.Count == 1 && SelectedDates.First() != dateTime.Date))
575659
{
576660
SelectedDates.Replace(dateTime.Date);
577661
}
@@ -593,7 +677,38 @@ public virtual void ChangeDateSelection(DateTime dateTime)
593677
}
594678
else if (dateTime != RangeSelectionStart)
595679
{
596-
RangeSelectionEnd = dateTime;
680+
bool isCorrectDirection;
681+
682+
switch (SelectionDirection)
683+
{
684+
case SelectionDirection.Any:
685+
isCorrectDirection = true;
686+
break;
687+
688+
case SelectionDirection.StartToEnd:
689+
isCorrectDirection = startSelectedDate == null || dateTime.Date >= startSelectedDate.Value.Date;
690+
break;
691+
692+
case SelectionDirection.EndToStart:
693+
isCorrectDirection = endSelectedDate == null || dateTime.Date <= endSelectedDate.Value.Date;
694+
break;
695+
696+
case SelectionDirection.Confined:
697+
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || (dateTime.Date >= startSelectedDate.Value.Date && dateTime.Date <= endSelectedDate.Value.Date);
698+
break;
699+
700+
case SelectionDirection.ConfinedReverse:
701+
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || dateTime.Date <= startSelectedDate.Value.Date || dateTime.Date >= endSelectedDate.Value.Date;
702+
break;
703+
704+
default:
705+
throw new NotImplementedException();
706+
}
707+
708+
if (isCorrectDirection)
709+
{
710+
RangeSelectionEnd = dateTime;
711+
}
597712
}
598713
break;
599714

XCalendarFormsSample/XCalendarFormsSample/Helpers/PopupHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static class PopupHelper
1919
public static List<NavigationLoopMode> AllNavigationLoopModes { get; set; } = Enum.GetValues(typeof(NavigationLoopMode)).Cast<NavigationLoopMode>().ToList();
2020
public static List<DayOfWeek> AllDaysOfWeek { get; set; } = DayOfWeek.Monday.GetWeekAsFirst();
2121
public static List<StackOrientation> AllStackOrientations { get; set; } = Enum.GetValues(typeof(StackOrientation)).Cast<StackOrientation>().ToList();
22+
public static List<SelectionDirection> AllSelectionDirections { get; set; } = Enum.GetValues(typeof(SelectionDirection)).Cast<SelectionDirection>().ToList();
2223
#endregion
2324

2425
#region Methods

XCalendarFormsSample/XCalendarFormsSample/ViewModels/PlaygroundViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public class PlaygroundViewModel : BaseViewModel
103103
public ICommand ChangeCalendarVisibilityCommand { get; set; }
104104
public ICommand UpdateCurrentCultureCommand { get; set; }
105105
public ICommand ShowDayEventsOrientationDialogCommand { get; set; }
106+
public ICommand ShowSelectionDirectionDialogCommand { get; set; }
106107

107108
#endregion
108109

@@ -136,6 +137,7 @@ public PlaygroundViewModel()
136137
ChangeCalendarVisibilityCommand = new Command<bool>(ChangeCalendarVisibility);
137138
UpdateCurrentCultureCommand = new Command(UpdateCurrentCulture);
138139
ShowDayEventsOrientationDialogCommand = new Command(ShowDayEventsOrientationDialog);
140+
ShowSelectionDirectionDialogCommand = new Command(ShowSelectionDirectionDialog);
139141

140142
List<ColoredEvent> events = new List<ColoredEvent>()
141143
{
@@ -384,6 +386,10 @@ public async void ShowDayEventsOrientationDialog()
384386
{
385387
DayEventsOrientation = await PopupHelper.ShowSelectItemDialogAsync(DayEventsOrientation, PopupHelper.AllStackOrientations);
386388
}
389+
public async void ShowSelectionDirectionDialog()
390+
{
391+
Calendar.SelectionDirection = await PopupHelper.ShowSelectItemDialogAsync(Calendar.SelectionDirection, PopupHelper.AllSelectionDirections);
392+
}
387393
#endregion
388394
}
389395
}

XCalendarFormsSample/XCalendarFormsSample/Views/PlaygroundPage.xaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,28 @@
225225
</Label>
226226
</Grid>
227227

228+
<Grid>
229+
<Label
230+
Grid.Column="0"
231+
FontSize="{StaticResource SmallFontSize}"
232+
HorizontalTextAlignment="Center"
233+
Text="SelectionDirection"
234+
VerticalTextAlignment="Center"/>
235+
<Label
236+
Grid.Column="1"
237+
FontSize="{StaticResource SmallFontSize}"
238+
HorizontalTextAlignment="Center"
239+
Text="{Binding Calendar.SelectionDirection}"
240+
TextColor="{StaticResource TappableSettingTextColor}"
241+
VerticalTextAlignment="Center">
242+
243+
<Label.GestureRecognizers>
244+
<TapGestureRecognizer Command="{Binding ShowSelectionDirectionDialogCommand}"/>
245+
</Label.GestureRecognizers>
246+
247+
</Label>
248+
</Grid>
249+
228250
<Grid>
229251
<Label
230252
Grid.Column="0"

XCalendarMauiSample/Helpers/PopupHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static class PopupHelper
1414
public static List<NavigationLoopMode> AllNavigationLoopModes { get; set; } = Enum.GetValues(typeof(NavigationLoopMode)).Cast<NavigationLoopMode>().ToList();
1515
public static List<DayOfWeek> AllDaysOfWeek { get; set; } = DayOfWeek.Monday.GetWeekAsFirst();
1616
public static List<StackOrientation> AllStackOrientations { get; set; } = Enum.GetValues(typeof(StackOrientation)).Cast<StackOrientation>().ToList();
17+
public static List<SelectionDirection> AllSelectionDirections { get; set; } = Enum.GetValues(typeof(SelectionDirection)).Cast<SelectionDirection>().ToList();
1718
#endregion
1819

1920
#region Methods

XCalendarMauiSample/ViewModels/PlaygroundViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class PlaygroundViewModel : BaseViewModel
7474
#region Commands
7575
public ICommand ShowCustomDayNamesOrderDialogCommand { get; set; }
7676
public ICommand ShowSelectionActionDialogCommand { get; set; }
77+
public ICommand ShowSelectionDirectionDialogCommand { get; set; }
7778
public ICommand ShowNavigationLoopModeDialogCommand { get; set; }
7879
public ICommand ShowNavigationTimeUnitDialogCommand { get; set; }
7980
public ICommand ShowPageStartModeDialogCommand { get; set; }
@@ -131,6 +132,7 @@ public PlaygroundViewModel()
131132
ChangeCalendarVisibilityCommand = new Command<bool>(ChangeCalendarVisibility);
132133
UpdateCurrentCultureCommand = new Command(UpdateCurrentCulture);
133134
ShowDayEventsOrientationDialogCommand = new Command(ShowDayEventsOrientationDialog);
135+
ShowSelectionDirectionDialogCommand = new Command(ShowSelectionDirectionDialog);
134136

135137
List<ColoredEvent> events = new List<ColoredEvent>()
136138
{
@@ -379,6 +381,10 @@ public async void ShowDayEventsOrientationDialog()
379381
{
380382
DayEventsOrientation = await PopupHelper.ShowSelectItemDialogAsync(DayEventsOrientation, PopupHelper.AllStackOrientations);
381383
}
384+
public async void ShowSelectionDirectionDialog()
385+
{
386+
Calendar.SelectionDirection = await PopupHelper.ShowSelectItemDialogAsync(Calendar.SelectionDirection, PopupHelper.AllSelectionDirections);
387+
}
382388
#endregion
383389
}
384390
}

XCalendarMauiSample/Views/PlaygroundPage.xaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,28 @@
222222
</Label>
223223
</Grid>
224224

225+
<Grid>
226+
<Label
227+
Grid.Column="0"
228+
FontSize="{StaticResource SmallFontSize}"
229+
HorizontalTextAlignment="Center"
230+
Text="SelectionDirection"
231+
VerticalTextAlignment="Center"/>
232+
<Label
233+
Grid.Column="1"
234+
FontSize="{StaticResource SmallFontSize}"
235+
HorizontalTextAlignment="Center"
236+
Text="{Binding Calendar.SelectionDirection}"
237+
TextColor="{StaticResource TappableSettingTextColor}"
238+
VerticalTextAlignment="Center">
239+
240+
<Label.GestureRecognizers>
241+
<TapGestureRecognizer Command="{Binding ShowSelectionDirectionDialogCommand}"/>
242+
</Label.GestureRecognizers>
243+
244+
</Label>
245+
</Grid>
246+
225247
<Grid>
226248
<Label
227249
Grid.Column="0"

0 commit comments

Comments
 (0)