forked from grijjy/MvvmStarterKit
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathGrijjy.Mvvm.DataBinding.Collections.pas
221 lines (182 loc) · 6.63 KB
/
Grijjy.Mvvm.DataBinding.Collections.pas
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
unit Grijjy.Mvvm.DataBinding.Collections;
{$INCLUDE 'Grijjy.inc'}
interface
uses
Grijjy.System,
Grijjy.Mvvm.Types;
type
{ Abstract base class for views that implement the IgoCollectionView
interface.
NOTE: This class implements an interface but is not reference counted. }
TgoCollectionView = class abstract(TgoNonRefCountedObject, IgoCollectionView)
{$REGION 'Internal Declarations'}
private
FSource: TgoCollectionSource; // Reference
FTemplate: TgoDataTemplateClass;
private
procedure AddItemsToView;
procedure UpdateItemsInView;
private
procedure CollectionChanged(const ASender: TObject;
const AArg: TgoCollectionChangedEventArgs);
protected
{ IgoCollectionView }
function GetSource: TgoCollectionSource;
procedure SetSource(const AValue: TgoCollectionSource);
function GetTemplate: TgoDataTemplateClass;
procedure SetTemplate(const AValue: TgoDataTemplateClass);
{$ENDREGION 'Internal Declarations'}
protected
{ Must be overridden to clear all items in the view.
For example, when used with a TListBox, it would call the TListBox.Clear
method. }
procedure ClearItemsInView; virtual; abstract;
{ Must be overridden to inform the view that a batch of updates will follow.
For example, when used with a TListBox, it would call the
TListBox.BeginUpdate method. }
procedure BeginUpdateView; virtual; abstract;
{ Must be overridden to inform the view that a batch of updates has ended.
For example, when used with a TListBox, it would call the
TListBox.EndUpdate method. }
procedure EndUpdateView; virtual; abstract;
{ Must be overridden to add an item to the view.
Parameters:
AItem: the item to add to the view. The type of the item is the same as
the type of the objects in the Source collection.
For example, when used with a TListBox, it would create a TListBoxItem
object, associate it with AItem, and add it to the list box. }
procedure AddItemToView(const AItem: TObject); virtual; abstract;
{ Must be overridden to delete an item from the view.
Parameters:
AItemIndex: the index of the item to delete.
For example, when used with a TListBox, it would delete item AItemIndex
from the list box. }
procedure DeleteItemFromView(const AItemIndex: Integer); virtual; abstract;
{ Must be overridden to update an item in the view.
Parameters:
AItem: the item that has changed. The type of the item is the same as
the type of the objects in the Source collection.
APropertyName: the name of the property of AItem that has changed.
For example, when used with a TListBox, it would find the TListBoxItem
associated with AItem and change one of its properties. }
procedure UpdateItemInView(const AItem: TObject;
const APropertyName: String); virtual; abstract;
{ Must be overridden to update all items in the view with new data.
This method is called when the order of the items in the source collection
has changed (for example, by sorting).
The number of items is unchanged, so the view doesn't have to add or
delete items. For example, when used with a TListBox, the list box would
update all existing TListBoxItem objects with the properties from the
corresponding items in the source collection. It also needs to
re-associate each TListBoxItem with the corresponding item in the
collection. }
procedure UpdateAllItemsInView; virtual; abstract;
public
{ The collection to show in the view. This can be any class derived from
TEnumerable<T>, as long is T is a class type. You must typecast it to
TgoCollectionSource to set the property.
(In technical terms: TList<TPerson> is convariant, meaning that it is
convertible to TList<TObject> if TPerson is a class. However, Delphi
does not support covariance (and contravariance) with generics, so you
need to typecast to TgoCollectionSource yourself.) }
property Source: TgoCollectionSource read FSource write SetSource;
{ The class that is used as a template to map items in the collection to
properties of items in the view. }
property Template: TgoDataTemplateClass read FTemplate write FTemplate;
end;
implementation
uses
System.SysUtils;
{ TgoCollectionView }
procedure TgoCollectionView.AddItemsToView;
var
Item: TObject;
begin
if (FSource = nil) then
Exit;
BeginUpdateView;
try
for Item in Source do
AddItemToView(Item);
finally
EndUpdateView;
end;
end;
procedure TgoCollectionView.CollectionChanged(const ASender: TObject;
const AArg: TgoCollectionChangedEventArgs);
begin
if (FTemplate = nil) then
Exit;
case AArg.Action of
TgoCollectionChangedAction.Add:
AddItemToView(AArg.Item);
TgoCollectionChangedAction.Delete:
DeleteItemFromView(AArg.ItemIndex);
TgoCollectionChangedAction.ItemChange:
UpdateItemInView(AArg.Item, AArg.PropertyName);
TgoCollectionChangedAction.Clear:
ClearItemsInView;
TgoCollectionChangedAction.Rearrange:
begin
BeginUpdateView;
try
UpdateAllItemsInView;
finally
EndUpdateView;
end;
end;
end;
end;
function TgoCollectionView.GetSource: TgoCollectionSource;
begin
Result := FSource;
end;
function TgoCollectionView.GetTemplate: TgoDataTemplateClass;
begin
Result := FTemplate;
end;
procedure TgoCollectionView.SetSource(const AValue: TgoCollectionSource);
var
NCC: IgoNotifyCollectionChanged;
begin
if (AValue <> FSource) then
begin
{ Unsubscribe from collection changed event of old source. }
if Assigned(FSource) and (Supports(FSource, IgoNotifyCollectionChanged, NCC)) then
NCC.GetCollectionChangedEvent.Remove(CollectionChanged);
FSource := AValue;
if Assigned(FTemplate) then
begin
ClearItemsInView;
AddItemsToView;
end;
{ Subscribe to collection changed event of new source. }
if Assigned(FSource) and (Supports(FSource, IgoNotifyCollectionChanged, NCC)) then
NCC.GetCollectionChangedEvent.Add(CollectionChanged);
end;
end;
procedure TgoCollectionView.SetTemplate(const AValue: TgoDataTemplateClass);
var
PrevTemplate: TgoDataTemplateClass;
begin
if (AValue <> FTemplate) then
begin
PrevTemplate := FTemplate;
FTemplate := AValue;
if Assigned(FTemplate) and Assigned(FSource) then
begin
if Assigned(PrevTemplate) then
UpdateItemsInView
else
begin
ClearItemsInView;
AddItemsToView;
end;
end;
end;
end;
procedure TgoCollectionView.UpdateItemsInView;
begin
Assert(False);
end;
end.