ListModel.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Linq.Expressions;
  8. using System.Reflection;
  9. using InABox.Core;
  10. using System.Diagnostics.CodeAnalysis;
  11. using System.Threading.Tasks;
  12. namespace InABox.Mobile
  13. {
  14. public abstract class ListModel<TParent, TItem, TEntity> : Model<TParent,TItem,TEntity>, IListModel<TParent, TItem, TEntity>, IEnumerable<TItem>
  15. where TParent : ListModel<TParent, TItem, TEntity>
  16. where TEntity : Entity, IRemotable, IPersistent, new()
  17. where TItem : Shell<TParent,TEntity>, new()
  18. {
  19. protected ListModel(IModelHost host, Func<Filter<TEntity>> filter, bool transient = false) : base(host, filter, transient)
  20. {
  21. Reset();
  22. }
  23. protected ListModel(IModelHost host, Func<Filter<TEntity>> filter, [NotNull] String filename) : base(host, filter, filename)
  24. {
  25. Reset();
  26. }
  27. protected override void Initialize()
  28. {
  29. _allitems = null;
  30. _items.Clear();
  31. }
  32. public virtual SortOrder<TEntity> Sort => LookupFactory.DefineSort<TEntity>();
  33. private IList<TItem> _allitems;
  34. private CoreTable _table = new CoreTable();
  35. private readonly CoreObservableCollection<TItem> _items = new CoreObservableCollection<TItem>();
  36. public IList<TItem> Items => _items;
  37. IEnumerable IListModel.Items => this.Items;
  38. public Func<TItem, bool> SearchPredicate { get; set; }
  39. public void Search(Func<TItem, bool> predicate)
  40. {
  41. SearchPredicate = predicate;
  42. Search();
  43. }
  44. public void Search()
  45. {
  46. var items = SearchPredicate != null
  47. ? new List<TItem>(_allitems.Where(SearchPredicate))
  48. : new List<TItem>(_allitems);
  49. _items.Clear();
  50. _items.ReplaceRange(items);
  51. OnPropertyChanged(nameof(Items));
  52. }
  53. protected virtual Expression<Func<TEntity, object>> ImageColumn => null;
  54. public override void BeforeLoad(MultiQuery query)
  55. {
  56. }
  57. public override void AfterLoad(MultiQuery query)
  58. {
  59. }
  60. public override IModel Refresh(bool force)
  61. {
  62. if (!Loaded || force)
  63. Load();
  64. return this;
  65. }
  66. public override void Refresh(bool force, Action loaded)
  67. {
  68. if (!Loaded || force)
  69. Load(loaded);
  70. else
  71. loaded?.Invoke();
  72. }
  73. public override void Load(Action loaded = null)
  74. {
  75. MultiQuery query = new MultiQuery();
  76. query.Add(
  77. Filter(),
  78. GetColumns<TItem,TEntity>(),
  79. Sort
  80. );
  81. if (ImageColumn != null)
  82. {
  83. query.Add<Document>(
  84. new Filter<Document>(x => x.ID).InQuery(Filter(), ImageColumn),
  85. new Columns<Document>(x => x.ID)
  86. .Add(x => x.Data)
  87. );
  88. }
  89. BeforeLoad(query);
  90. // If we have a valid transport, always try and get new data from the server
  91. if (Host.IsConnected())
  92. {
  93. if (loaded != null)
  94. query.Query((q) =>
  95. {
  96. if (Type == ModelType.Persistent)
  97. SaveToStorage(q);
  98. DoAfterLoad(q, loaded);
  99. });
  100. else
  101. {
  102. query.Query();
  103. if (Type == ModelType.Persistent)
  104. SaveToStorage(query);
  105. DoAfterLoad(query);
  106. }
  107. }
  108. else
  109. {
  110. if (Type == ModelType.Transient)
  111. {
  112. InitializeTables(query);
  113. }
  114. else if (Type == ModelType.Normal)
  115. {
  116. // Only load
  117. if (_allitems == null)
  118. InitializeTables(query);
  119. }
  120. else if (Type == ModelType.Persistent)
  121. {
  122. // Treat it as normal, unless its the first time through
  123. // in which case try to load it from storage, if the
  124. // data has been previously cached
  125. if (_allitems == null)
  126. LoadFromStorage(query);
  127. }
  128. DoAfterLoad(query, loaded);
  129. }
  130. }
  131. private void DoAfterLoad(MultiQuery query, Action loaded = null)
  132. {
  133. _table = query.Get<TEntity>();
  134. _allitems = new List<TItem>(query.Get<TEntity>().Rows.Select(row => CreateItem<TItem>(row)));
  135. if (ImageColumn != null)
  136. {
  137. Images.Clear();
  138. query.Get<Document>().IntoDictionary<Document, Guid, byte[]>(Images, x => x.ID,
  139. r => r.Get<Document, byte[]>(x => x.Data));
  140. }
  141. try
  142. {
  143. Search();
  144. }
  145. catch (Exception e)
  146. {
  147. }
  148. try
  149. {
  150. AfterLoad(query);
  151. }
  152. catch (Exception e)
  153. {
  154. }
  155. try
  156. {
  157. Loaded = true;
  158. }
  159. catch (Exception e)
  160. {
  161. }
  162. try
  163. {
  164. loaded?.Invoke();
  165. }
  166. catch (Exception e)
  167. {
  168. }
  169. try
  170. {
  171. NotifyChanged();
  172. }
  173. catch (Exception e)
  174. {
  175. }
  176. }
  177. protected T CreateItem<T>(CoreRow row)
  178. where T : Shell<TParent,TEntity>, new()
  179. {
  180. var result = new T() { Row = row, Parent = (TParent)this };
  181. result.PropertyChanged += (sender, args) => DoPropertyChanged(result, args);
  182. return result;
  183. }
  184. public virtual TItem CreateItem()
  185. {
  186. CoreRow row = _table.NewRow();
  187. var result = CreateItem<TItem>(row);
  188. ItemAdded?.Invoke(this, new ListModelItemCreatedArgs<TItem>(result));
  189. return result;
  190. }
  191. public virtual void CommitItem(TItem item)
  192. {
  193. _table.Rows.Add(item.Row);
  194. _allitems.Add(item);
  195. Search(null);
  196. NotifyChanged();
  197. }
  198. public virtual TItem AddItem()
  199. {
  200. var result = CreateItem();
  201. CommitItem(result);
  202. return result;
  203. }
  204. public virtual void DeleteItem(TItem item)
  205. {
  206. _table.Rows.Remove(item.Row);
  207. _allitems.Remove(item);
  208. Search(null);
  209. NotifyChanged();
  210. }
  211. object IListModel.CreateItem() => this.CreateItem();
  212. void IListModel.CommitItem(object item)
  213. {
  214. if (item is TItem titem)
  215. CommitItem(titem);
  216. }
  217. object IListModel.AddItem() => this.AddItem();
  218. void IListModel.DeleteItem(object item)
  219. {
  220. if (item is TItem titem)
  221. DeleteItem(titem);
  222. }
  223. IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
  224. {
  225. return Items.GetEnumerator();
  226. }
  227. public IEnumerator GetEnumerator()
  228. {
  229. return Items.GetEnumerator();
  230. }
  231. public event ListModelItemCreatedEvent<TItem> ItemAdded;
  232. }
  233. }