Skip to content

Commit cad0305

Browse files
committed
Add "ComplexCalendar" to UI framework projects
This calendar has a default implementation for events.
1 parent 0bbb57a commit cad0305

29 files changed

+995
-361
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using XCalendar.Core.Collections;
2+
using XCalendar.Core.Interfaces;
3+
4+
namespace XCalendar.Forms.Interfaces
5+
{
6+
public interface IComplexCalendarDay : ICalendarDay
7+
{
8+
ObservableRangeCollection<IEvent> Events { get; }
9+
}
10+
}

XCalendar.Forms/Interfaces/IEvent.cs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using Xamarin.Forms;
3+
4+
namespace XCalendar.Forms.Interfaces
5+
{
6+
public interface IEvent
7+
{
8+
string Title { get; set; }
9+
string Description { get; set; }
10+
DateTime DateTime { get; set; }
11+
Color Color { get; set; }
12+
}
13+
}
+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Specialized;
4+
using System.Linq;
5+
using XCalendar.Core.Collections;
6+
using XCalendar.Core.Models;
7+
using XCalendar.Forms.Interfaces;
8+
9+
namespace XCalendar.Forms.Models
10+
{
11+
public class ComplexCalendar : ComplexCalendar<ComplexCalendarDay, Event>
12+
{
13+
}
14+
public class ComplexCalendar<T, TEvent> : Calendar<T> where T : IComplexCalendarDay, new() where TEvent : IEvent
15+
{
16+
#region Fields
17+
private ObservableRangeCollection<TEvent> _events = new ObservableRangeCollection<TEvent>();
18+
#endregion
19+
20+
#region Properties
21+
public ObservableRangeCollection<TEvent> Events
22+
{
23+
get
24+
{
25+
return _events;
26+
}
27+
set
28+
{
29+
if (_events != value)
30+
{
31+
if (_events != null)
32+
{
33+
_events.CollectionChanged -= Events_CollectionChanged;
34+
}
35+
36+
if (value != null)
37+
{
38+
value.CollectionChanged += Events_CollectionChanged;
39+
}
40+
41+
_events = value;
42+
OnPropertyChanged();
43+
}
44+
}
45+
}
46+
#endregion
47+
48+
#region Constructors
49+
public ComplexCalendar()
50+
{
51+
Events = new ObservableRangeCollection<TEvent>();
52+
}
53+
#endregion
54+
55+
#region Methods
56+
public override void UpdateDay(T day, DateTime newDateTime)
57+
{
58+
base.UpdateDay(day, newDateTime);
59+
UpdateDayEvents(day);
60+
}
61+
public virtual void UpdateDayEvents(T day)
62+
{
63+
//It is known that the only thing that the events of the day depend on is the DateTime of the day, and that all events will have the same date.
64+
//So, only update the events if the existing ones' DateTime does not match the day's DateTime.
65+
//If the day has no events, there is no way to tell if it's because the day hasn't been updated before or if there are no events with that date, so update it either way.
66+
if (day.Events.Count > 0 && day.DateTime.Date == day.Events[0].DateTime.Date)
67+
{
68+
return;
69+
}
70+
71+
IEnumerable<TEvent> events = Events.Where(x => x.DateTime.Date == day.DateTime.Date);
72+
73+
//No use in replacing the collection if the source and target are both empty.
74+
if (day.Events.Count == 0 && !events.Any())
75+
{
76+
return;
77+
}
78+
79+
IEnumerable<IEvent> castedEvents = events.Cast<IEvent>();
80+
81+
//SequenceEqual could be omitted to improve performance but in the vast majority of cases there won't even be more than 5 events in one day, so impact on performance should be negligible
82+
//compared to always changing the collection and updating the binding.
83+
if (day.Events.SequenceEqual(castedEvents))
84+
{
85+
return;
86+
}
87+
88+
day.Events.ReplaceRange(castedEvents);
89+
}
90+
private void Events_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
91+
{
92+
UpdateDays(NavigatedDate);
93+
}
94+
#endregion
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using XCalendar.Core.Collections;
2+
using XCalendar.Core.Models;
3+
using XCalendar.Forms.Interfaces;
4+
5+
namespace XCalendar.Forms.Models
6+
{
7+
public class ComplexCalendarDay : CalendarDay, IComplexCalendarDay
8+
{
9+
#region Fields
10+
private ObservableRangeCollection<IEvent> _events = new ObservableRangeCollection<IEvent>();
11+
#endregion
12+
13+
#region Properties
14+
public ObservableRangeCollection<IEvent> Events
15+
{
16+
get
17+
{
18+
return _events;
19+
}
20+
set
21+
{
22+
if (_events != value)
23+
{
24+
_events = value;
25+
OnPropertyChanged();
26+
}
27+
}
28+
}
29+
#endregion
30+
}
31+
}

XCalendar.Forms/Models/Event.cs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.Runtime.CompilerServices;
4+
using Xamarin.Forms;
5+
using XCalendar.Forms.Interfaces;
6+
7+
namespace XCalendar.Forms.Models
8+
{
9+
public class Event : IEvent, INotifyPropertyChanged
10+
{
11+
#region Fields
12+
private string _title;
13+
private string _description;
14+
private DateTime _dateTime = DateTime.Today;
15+
private Color _color;
16+
#endregion
17+
18+
#region Properties
19+
public string Title
20+
{
21+
get
22+
{
23+
return _title;
24+
}
25+
set
26+
{
27+
if (_title != value)
28+
{
29+
_title = value;
30+
OnPropertyChanged();
31+
}
32+
}
33+
}
34+
public string Description {
35+
get
36+
{
37+
return _description;
38+
}
39+
set
40+
{
41+
if (_description != value)
42+
{
43+
_description = value;
44+
OnPropertyChanged();
45+
}
46+
}
47+
}
48+
public DateTime DateTime
49+
{
50+
get
51+
{
52+
return _dateTime;
53+
}
54+
set
55+
{
56+
if (_dateTime != value)
57+
{
58+
_dateTime = value;
59+
OnPropertyChanged();
60+
}
61+
}
62+
}
63+
public Color Color
64+
{
65+
get
66+
{
67+
return _color;
68+
}
69+
set
70+
{
71+
if (_color != value)
72+
{
73+
_color = value;
74+
OnPropertyChanged();
75+
}
76+
}
77+
}
78+
79+
#endregion
80+
81+
#region Events
82+
public event PropertyChangedEventHandler PropertyChanged;
83+
#endregion
84+
85+
#region Methods
86+
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
87+
{
88+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
89+
}
90+
#endregion
91+
}
92+
}

XCalendar.Forms/Views/DayView.xaml

+89-16
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,99 @@
33
x:Class="XCalendar.Forms.Views.DayView"
44
xmlns="http://xamarin.com/schemas/2014/forms"
55
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
6+
xmlns:Converters="clr-namespace:XCalendar.Forms.Converters"
7+
xmlns:Interfaces="clr-namespace:XCalendar.Forms.Interfaces"
8+
xmlns:System="clr-namespace:System;assembly=System.Runtime"
69
xmlns:xc="clr-namespace:XCalendar.Forms.Views"
710
x:Name="DayView_Unique"
811
x:DataType="{x:Type xc:DayView}">
912

10-
<Label
11-
CharacterSpacing="{Binding CharacterSpacing, Source={x:Reference DayView_Unique}}"
12-
FontAttributes="{Binding FontAttributes, Source={x:Reference DayView_Unique}}"
13-
FontFamily="{Binding FontFamily, Source={x:Reference DayView_Unique}}"
14-
FontSize="{Binding FontSize, Source={x:Reference DayView_Unique}}"
15-
FormattedText="{Binding FormattedText, Source={x:Reference DayView_Unique}}"
16-
HorizontalTextAlignment="{Binding HorizontalTextAlignment, Source={x:Reference DayView_Unique}}"
17-
LineBreakMode="{Binding LineBreakMode, Source={x:Reference DayView_Unique}}"
18-
LineHeight="{Binding LineHeight, Source={x:Reference DayView_Unique}}"
19-
MaxLines="{Binding MaxLines, Source={x:Reference DayView_Unique}}"
20-
Text="{Binding Text, Source={x:Reference DayView_Unique}}"
21-
TextColor="{Binding TextColor, Source={x:Reference DayView_Unique}}"
22-
TextDecorations="{Binding TextDecorations, Source={x:Reference DayView_Unique}}"
23-
TextTransform="{Binding TextTransform, Source={x:Reference DayView_Unique}}"
24-
TextType="{Binding TextType, Source={x:Reference DayView_Unique}}"
25-
VerticalTextAlignment="{Binding VerticalTextAlignment, Source={x:Reference DayView_Unique}}"/>
13+
<Grid RowSpacing="2">
14+
15+
<Grid.RowDefinitions>
16+
<RowDefinition Height="1.5*"/>
17+
<RowDefinition Height="*"/>
18+
</Grid.RowDefinitions>
19+
20+
<Label
21+
Grid.Row="0"
22+
Grid.RowSpan="2"
23+
CharacterSpacing="{Binding CharacterSpacing, Source={x:Reference DayView_Unique}}"
24+
FontAttributes="{Binding FontAttributes, Source={x:Reference DayView_Unique}}"
25+
FontFamily="{Binding FontFamily, Source={x:Reference DayView_Unique}}"
26+
FontSize="{Binding FontSize, Source={x:Reference DayView_Unique}}"
27+
FormattedText="{Binding FormattedText, Source={x:Reference DayView_Unique}}"
28+
HorizontalTextAlignment="{Binding HorizontalTextAlignment, Source={x:Reference DayView_Unique}}"
29+
LineBreakMode="{Binding LineBreakMode, Source={x:Reference DayView_Unique}}"
30+
LineHeight="{Binding LineHeight, Source={x:Reference DayView_Unique}}"
31+
MaxLines="{Binding MaxLines, Source={x:Reference DayView_Unique}}"
32+
Text="{Binding Text, Source={x:Reference DayView_Unique}}"
33+
TextColor="{Binding TextColor, Source={x:Reference DayView_Unique}}"
34+
TextDecorations="{Binding TextDecorations, Source={x:Reference DayView_Unique}}"
35+
TextTransform="{Binding TextTransform, Source={x:Reference DayView_Unique}}"
36+
TextType="{Binding TextType, Source={x:Reference DayView_Unique}}"
37+
VerticalOptions="Center"
38+
VerticalTextAlignment="{Binding VerticalTextAlignment, Source={x:Reference DayView_Unique}}"/>
39+
40+
<ContentView Grid.Row="1" ControlTemplate="{Binding EventsTemplate, Source={x:Reference DayView_Unique}}">
41+
<StackLayout
42+
BindableLayout.ItemsSource="{Binding Events, Source={x:Reference DayView_Unique}}"
43+
HorizontalOptions="Center"
44+
Orientation="{Binding EventsOrientation, Source={x:Reference DayView_Unique}}"
45+
Spacing="{Binding EventsSpacing, Source={x:Reference DayView_Unique}}">
46+
47+
<StackLayout.Resources>
48+
<System:Boolean x:Key="TrueValue">True</System:Boolean>
49+
<System:Boolean x:Key="FalseValue">False</System:Boolean>
50+
51+
<Converters:IsNullOrEmptyConverter x:Key="IsNullOrEmptyConverter"/>
52+
</StackLayout.Resources>
53+
54+
<StackLayout.Style>
55+
<Style TargetType="{x:Type StackLayout}">
56+
<Setter Property="BindableLayout.ItemTemplate">
57+
<Setter.Value>
58+
<DataTemplate x:DataType="{x:Type Interfaces:IEvent}">
59+
<Rectangle
60+
Fill="{Binding Color}"
61+
HeightRequest="{Binding EventHeightRequest, Source={x:Reference DayView_Unique}}"
62+
HorizontalOptions="CenterAndExpand"
63+
RadiusX="{Binding EventCornerRadius, Source={x:Reference DayView_Unique}}"
64+
RadiusY="{Binding EventCornerRadius, Source={x:Reference DayView_Unique}}"
65+
VerticalOptions="Center"
66+
WidthRequest="{Binding EventWidthRequest, Source={x:Reference DayView_Unique}}"/>
67+
</DataTemplate>
68+
</Setter.Value>
69+
</Setter>
70+
71+
<Style.Triggers>
72+
<DataTrigger
73+
Binding="{Binding EventTemplate, Source={x:Reference DayView_Unique}, Converter={StaticResource IsNullOrEmptyConverter}}"
74+
TargetType="{x:Type StackLayout}"
75+
Value="{StaticResource FalseValue}">
76+
<Setter Property="BindableLayout.ItemTemplate" Value="{Binding EventTemplate, Source={x:Reference DayView_Unique}}"/>
77+
</DataTrigger>
78+
79+
<DataTrigger
80+
Binding="{Binding IsCurrentMonth, Source={x:Reference DayView_Unique}}"
81+
TargetType="{x:Type StackLayout}"
82+
Value="{StaticResource FalseValue}">
83+
<Setter Property="IsVisible" Value="False"/>
84+
</DataTrigger>
85+
86+
<DataTrigger
87+
Binding="{Binding IsInvalid, Source={x:Reference DayView_Unique}}"
88+
TargetType="{x:Type StackLayout}"
89+
Value="{StaticResource TrueValue}">
90+
<Setter Property="IsVisible" Value="False"/>
91+
</DataTrigger>
92+
</Style.Triggers>
93+
</Style>
94+
</StackLayout.Style>
95+
</StackLayout>
96+
</ContentView>
97+
98+
</Grid>
2699

27100
<ContentView.GestureRecognizers>
28101
<TapGestureRecognizer Command="{Binding Command, Source={x:Reference DayView_Unique}}" CommandParameter="{Binding CommandParameter, Source={x:Reference DayView_Unique}}"/>

0 commit comments

Comments
 (0)