-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
GroupedItemTemplateCollection.cs
177 lines (151 loc) · 4.7 KB
/
GroupedItemTemplateCollection.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading;
namespace Xamarin.Forms.Platform.UWP
{
internal class GroupedItemTemplateCollection : ObservableCollection<GroupTemplateContext>
{
readonly IEnumerable _itemsSource;
readonly DataTemplate _itemTemplate;
readonly DataTemplate _groupHeaderTemplate;
readonly DataTemplate _groupFooterTemplate;
readonly BindableObject _container;
readonly IList _groupList;
public GroupedItemTemplateCollection(IEnumerable itemsSource, DataTemplate itemTemplate,
DataTemplate groupHeaderTemplate, DataTemplate groupFooterTemplate, BindableObject container)
{
_itemsSource = itemsSource;
_itemTemplate = itemTemplate;
_groupHeaderTemplate = groupHeaderTemplate;
_groupFooterTemplate = groupFooterTemplate;
_container = container;
foreach (var group in _itemsSource)
{
var groupTemplateContext = CreateGroupTemplateContext(group);
Add(groupTemplateContext);
}
if (_itemsSource is IList groupList && _itemsSource is INotifyCollectionChanged incc)
{
_groupList = groupList;
incc.CollectionChanged += GroupsChanged;
}
}
GroupTemplateContext CreateGroupTemplateContext(object group)
{
var groupHeaderTemplateContext = _groupHeaderTemplate != null
? new ItemTemplateContext(_groupHeaderTemplate, group, _container) : null;
var groupFooterTemplateContext = _groupFooterTemplate != null
? new GroupFooterItemTemplateContext(_groupFooterTemplate, group, _container) : null;
// This is where we'll eventually look at GroupItemPropertyName
var groupItemsList = TemplatedItemSourceFactory.Create(group as IEnumerable, _itemTemplate, _container);
return new GroupTemplateContext(groupHeaderTemplateContext, groupFooterTemplateContext, groupItemsList);
}
void GroupsChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (_container.Dispatcher.IsInvokeRequired)
{
_container.Dispatcher.BeginInvokeOnMainThread(() => GroupsChanged(args));
}
else
{
GroupsChanged(args);
}
}
void GroupsChanged(NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
Add(args);
break;
case NotifyCollectionChangedAction.Move:
Move(args);
break;
case NotifyCollectionChangedAction.Remove:
Remove(args);
break;
case NotifyCollectionChangedAction.Replace:
Replace(args);
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
}
}
void Add(NotifyCollectionChangedEventArgs args)
{
var startIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _groupList.IndexOf(args.NewItems[0]);
var count = args.NewItems.Count;
for (int n = 0; n < count; n++)
{
Insert(startIndex, CreateGroupTemplateContext(args.NewItems[n]));
}
}
void Move(NotifyCollectionChangedEventArgs args)
{
var count = args.NewItems.Count;
if (args.OldStartingIndex > args.NewStartingIndex)
{
for (int n = 0; n < count; n++)
{
Move(args.OldStartingIndex + n, args.NewStartingIndex + n);
}
return;
}
for (int n = count - 1; n >= 0; n--)
{
Move(args.OldStartingIndex + n, args.NewStartingIndex + n);
}
}
void Remove(NotifyCollectionChangedEventArgs args)
{
var startIndex = args.OldStartingIndex;
if (startIndex < 0)
{
// INCC implementation isn't giving us enough information to know where the removed items were in the
// collection. So the best we can do is a full Reset.
Reset();
return;
}
var count = args.OldItems.Count;
for (int n = startIndex + count - 1; n >= startIndex; n--)
{
RemoveAt(n);
}
}
void Replace(NotifyCollectionChangedEventArgs args)
{
var newItemCount = args.NewItems.Count;
if (newItemCount == args.OldItems.Count)
{
for (int n = 0; n < newItemCount; n++)
{
var index = args.OldStartingIndex + n;
var oldItem = this[index];
var newItem = CreateGroupTemplateContext(args.NewItems[0]);
Items[index] = newItem;
var update = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index);
OnCollectionChanged(update);
}
}
else
{
// If we're replacing one set with an equal size set, we can do a soft reset; if not, we have to completely
// rebuild the collection
Reset();
}
}
void Reset()
{
Items.Clear();
foreach (var group in _itemsSource)
{
var groupTemplateContext = CreateGroupTemplateContext(group);
Items.Add(groupTemplateContext);
}
var reset = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(reset);
}
}
}