forked from Clancey/MonoDroid.Dialog
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Section.cs
405 lines (355 loc) · 12.5 KB
/
Section.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
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
using System;
using System.Collections;
using System.Collections.Generic;
using Android.Content;
using Android.Views;
using Android.Widget;
namespace Android.Dialog
{
/// <summary>
/// Sections contain individual Element instances that are rendered by Android.Dialog
/// </summary>
/// <remarks>
/// Sections are used to group elements in the screen and they are the
/// only valid direct child of the RootElement. Sections can contain
/// any of the standard elements, including new RootElements.
///
/// RootElements embedded in a section are used to navigate to a new
/// deeper level.
///
/// You can assign a header and a footer either as strings (Header and Footer)
/// properties, or as ViewElements to be shown (HeaderView and FooterView). Internally
/// this uses the same storage, so you can only show one or the other.
/// </remarks>
public class Section : Element, IEnumerable<Element>
{
public List<Element> Elements = new List<Element>();
private readonly List<string> ElementTypes = new List<string>();
private object footer;
private object header;
/// <summary>
/// Constructs a Section without header or footers and an hidden section block
/// </summary>
public Section()
: this((string)null) { }
/// <summary>
/// Constructs a Section with the specified header
/// </summary>
/// <param name="caption">
/// The header to display
/// </param>
public Section(string caption)
: base(caption)
{
}
/// <summary>
/// Constructs a Section with a header and a footer
/// </summary>
/// <param name="caption">
/// The caption to display (or null to not display a caption)
/// </param>
/// <param name="footer">
/// The footer to display.
/// </param>
public Section(string caption, string footer)
: this(caption)
{
Footer = footer;
}
/// <summary>
/// Initializes a new instance of the <see cref="Section"/> class.
/// </summary>
/// <param name="header">The header as an <see cref="Element"/>.</param>
/// <remarks>The header can be customized as a custom <see cref="View"/> with a <see cref="ViewElement"/></remarks>
public Section(Element header)
: this()
{
HeaderView = header;
}
/// <summary>
/// Initializes a new instance of the <see cref="Section"/> class.
/// </summary>
/// <param name="header">The header, either a <see cref="String"/> for a simple header, or a custom <see cref="Element"/> (likely a <see cref="ViewElement"/>).</param>
/// <param name="footer">The footer, either a <see cref="String"/> for a simple footer, or a custom <see cref="Element"/> (likely a <see cref="ViewElement"/>).</param>
public Section(object header, object footer)
: this()
{
this.header = header;
this.footer = footer;
}
/// <summary>
/// The section header, as a string
/// </summary>
public string Header
{
get { return Caption; }
set { Caption = value; }
}
/// <summary>
/// The section footer, as a string.
/// </summary>
public string Footer
{
get { return footer == null ? null : footer.ToString(); }
set { footer = value; }
}
/// <summary>
/// The section's header view.
/// </summary>
public Element HeaderView
{
get { return header as Element; }
set { header = value; }
}
/// <summary>
/// The section's footer view.
/// </summary>
public Element FooterView
{
get { return footer as Element; }
set { footer = value; }
}
public int Count
{
get { return Elements.Count; }
}
public Element this[int idx]
{
get { return Elements[idx]; }
}
public event EventHandler ValueChanged;
private void HandleValueChangedEvent(object sender, EventArgs args)
{
if (ValueChanged != null)
ValueChanged(sender, args);
}
/// <summary>
/// Adds a new child Element to the Section
/// </summary>
/// <param name="element">
/// An element to add to the section.
/// </param>
public void Add(Element element)
{
if (element == null)
return;
var elementType = element.GetType().FullName;
if (!ElementTypes.Contains(elementType))
ElementTypes.Add(elementType);
Elements.Add(element);
element.Parent = this;
// bind value changed to our local handler so section itself is aware of events, allows cascacding upward notifications
if (element is EntryElement)
(element as EntryElement).Changed += HandleValueChangedEvent;
else if (element is BooleanElement)
(element as BooleanElement).Changed += HandleValueChangedEvent;
else if (element is CheckboxElement)
(element as CheckboxElement).Changed += HandleValueChangedEvent;
else if (element is RootElement)
(element as RootElement).RadioSelectionChanged += HandleValueChangedEvent;
}
/// <summary>Add version that can be used with LINQ</summary>
/// <param name="elements">
/// An enumerable list that can be produced by something like:
/// from x in ... select (Element) new MyElement (...)
/// </param>
public int Add(IEnumerable<Element> elements)
{
int count = 0;
foreach (Element e in elements)
{
Add(e);
count++;
}
return count;
}
/// <summary>
/// Inserts a series of elements into the Section
/// </summary>
/// <param name="idx">
/// The index where the elements are inserted
/// </param>
/// <param name="newElements">
/// A series of elements.
/// </param>
public void Insert(int idx, params Element[] newElements)
{
if (newElements == null)
return;
foreach (var e in newElements)
{
Elements.Insert(idx++, e);
e.Parent = this;
}
}
/// <summary>
/// Inserts a <see cref="IEnumerable{T}"/> of Elements into the Section
/// </summary>
/// <param name="idx">The index to insert the elements.</param>
/// <param name="newElements">A series of elements.</param>
/// <returns></returns>
public int Insert(int idx, IEnumerable<Element> newElements)
{
if (newElements == null)
return 0;
int count = 0;
foreach (var e in newElements)
{
Elements.Insert(idx++, e);
e.Parent = this;
count++;
}
return count;
}
public void Remove(Element e)
{
if (e == null)
return;
for (int i = Elements.Count; i > 0; )
{
i--;
if (Elements[i] != e) continue;
RemoveRange(i, 1);
return;
}
}
public void Remove(int idx)
{
RemoveRange(idx, 1);
}
/// <summary>
/// Removes a range of elements from the Section
/// </summary>
/// <param name="start">
/// Starting position
/// </param>
/// <param name="count">
/// Number of elements to remove from the section
/// </param>
public void RemoveRange(int start, int count)
{
if (start < 0 || start >= Elements.Count)
return;
if (count == 0)
return;
if (start + count > Elements.Count)
count = Elements.Count - start;
Elements.RemoveRange(start, count);
//var root = Parent as RootElement;
//if (root == null)
// return;
//int sidx = root.IndexOf(this);
//var paths = new NSIndexPath[count];
//for (int i = 0; i < count; i++)
// paths[i] = NSIndexPath.FromRowSection(start + i, sidx);
//root.TableView.DeleteRows(paths, anim);
}
public void Clear()
{
foreach (Element e in Elements)
e.Dispose();
Elements = new List<Element>();
//var root = Parent as RootElement;
//if (root != null && root.TableView != null)
// root.TableView.ReloadData();
}
protected override void Dispose(bool disposing)
{
if (!disposing) return;
Parent = null;
Clear();
Elements = null;
}
public int GetElementViewType(Element e)
{
var elementType = e.GetType().FullName;
for (int i = 0; i < ElementTypes.Count; i++)
{
if (ElementTypes[i].Equals(elementType))
return i + 1;
}
return 0;
}
public int ElementViewTypeCount
{
get { return ElementTypes.Count; }
}
public override View GetView(Context context, View convertView, ViewGroup parent)
{
if (HeaderView != null)
{
return HeaderView.GetView(context, convertView, parent);
}
if (Caption != null)
{
var view = (convertView as TextView) ?? new TextView(context, null, Android.Resource.Attribute.ListSeparatorTextViewStyle);
if (Caption.Length >= 0)
{
view.Text = Caption;
view.Visibility = ViewStates.Visible;
}
else
{
view.Text = string.Empty;
view.Visibility = ViewStates.Visible;
}
return view;
}
// invisible/empty section header, could be re-shown by setting the caption and refreshing the list
return new View(context, null)
{
LayoutParameters = new ListView.LayoutParams(ListView.LayoutParams.FillParent, 0),
Visibility = ViewStates.Gone,
};
}
public View GetFooterView(Context context, View convertView, ViewGroup parent)
{
if (FooterView != null)
{
return FooterView.GetView(context, convertView, parent); ;
}
if (Footer != null)
{
var view = (convertView as TextView) ?? new TextView(context);
view.Gravity = GravityFlags.Center;
if (Caption.Length >= 0)
{
view.Text = Footer;
view.Visibility = ViewStates.Visible;
}
else
{
view.Text = string.Empty;
view.Visibility = ViewStates.Visible;
}
return view;
}
// invisible/empty section footer, could be re-shown by setting the footer and refreshing the list
return new View(context, null)
{
LayoutParameters = new ListView.LayoutParams(ListView.LayoutParams.FillParent, 0),
Visibility = ViewStates.Gone,
};
}
/// <summary>
/// Enumerator to get all the elements in the Section.
/// </summary>
/// <returns>
/// A <see cref="IEnumerator{T}"/>
/// </returns>
public IEnumerator<Element> GetEnumerator()
{
return Elements.GetEnumerator();
}
/// <summary>
/// Enumerator to get all the elements in the Section.
/// </summary>
/// <returns>
/// A <see cref="IEnumerator{T}"/>
/// </returns>
IEnumerator IEnumerable.GetEnumerator()
{
return Elements.GetEnumerator();
}
}
}