ListModel.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using InABox.Core;
  8. using System.Diagnostics.CodeAnalysis;
  9. namespace comal.timesheets
  10. {
  11. public abstract class ListModel<TParent, TItem, TEntity> : Model<TParent,TItem,TEntity>, IListModel<TParent, TItem, TEntity>, IEnumerable<TItem>
  12. where TParent : ListModel<TParent, TItem, TEntity>
  13. where TEntity : Entity, IRemotable, IPersistent, new()
  14. where TItem : Shell<TParent,TEntity>, new()
  15. {
  16. protected ListModel(IModelHost host, Func<Filter<TEntity>> filter, bool transient = false) : base(host, filter, transient)
  17. {
  18. Reset();
  19. }
  20. protected ListModel(IModelHost host, Func<Filter<TEntity>> filter, [NotNull] String filename) : base(host, filter, filename)
  21. {
  22. Reset();
  23. }
  24. protected override void Initialize()
  25. {
  26. _allitems = null;
  27. _items.Clear();
  28. }
  29. public virtual SortOrder<TEntity> Sort => LookupFactory.DefineSort<TEntity>();
  30. private IEnumerable<TItem> _allitems;
  31. protected IList<TItem> _items = new List<TItem>();
  32. public IList<TItem> Items
  33. {
  34. get => _items;
  35. set => SetProperty(ref _items, value);
  36. }
  37. public void Search(Func<TItem, bool> predicate)
  38. {
  39. Items = predicate != null
  40. ? new List<TItem>(_allitems.Where(predicate))
  41. : new List<TItem>(_allitems);
  42. }
  43. protected virtual Expression<Func<TEntity, object>> ImageColumn => null;
  44. public override void BeforeLoad(MultiQuery query)
  45. {
  46. }
  47. public override void AfterLoad(MultiQuery query)
  48. {
  49. }
  50. private Columns<TEntity> DataColumns()
  51. {
  52. var prop = typeof(TItem).GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
  53. .FirstOrDefault(x => x.PropertyType == typeof(ShellColumns<TParent,TEntity>));
  54. var cols = prop?.GetValue(null) as ShellColumns<TParent,TEntity>;
  55. return cols?.Count > 0
  56. ? cols?.Columns
  57. : Columns;
  58. }
  59. public IListModel<TParent, TItem, TEntity> Refresh(bool force)
  60. {
  61. if (!Loaded || force)
  62. Load();
  63. return this;
  64. }
  65. public override void Refresh(bool force, Action loaded = null)
  66. {
  67. if (!Loaded || force)
  68. Load(loaded);
  69. else
  70. loaded?.Invoke();
  71. }
  72. public override void Load(Action loaded = null)
  73. {
  74. MultiQuery query = new MultiQuery();
  75. query.Add(
  76. Filter(),
  77. DataColumns(),
  78. Sort
  79. );
  80. if (ImageColumn != null)
  81. {
  82. query.Add<Document>(
  83. new Filter<Document>(x => x.ID).InQuery(Filter(), ImageColumn),
  84. new Columns<Document>(x => x.ID)
  85. .Add(x => x.Data)
  86. );
  87. }
  88. BeforeLoad(query);
  89. // If we have a valid transport, always try and get new data from the server
  90. if (Host.IsConnected())
  91. {
  92. if (loaded != null)
  93. query.Query((q) =>
  94. {
  95. if (Type == ModelType.Persistent)
  96. SaveToStorage(q);
  97. DoAfterLoad(q, loaded);
  98. });
  99. else
  100. {
  101. query.Query();
  102. if (Type == ModelType.Persistent)
  103. SaveToStorage(query);
  104. DoAfterLoad(query);
  105. }
  106. }
  107. else
  108. {
  109. if (Type == ModelType.Transient)
  110. {
  111. InitializeTables(query);
  112. }
  113. else if (Type == ModelType.Normal)
  114. {
  115. // Only load
  116. if (_allitems == null)
  117. InitializeTables(query);
  118. }
  119. else if (Type == ModelType.Persistent)
  120. {
  121. // Treat it as normal, unless its the first time through
  122. // in which case try to load it from storage, if the
  123. // data has been previously cached
  124. if (_allitems == null)
  125. LoadFromStorage(query);
  126. }
  127. DoAfterLoad(query, loaded);
  128. }
  129. }
  130. private void DoAfterLoad(MultiQuery query, Action loaded = null)
  131. {
  132. _allitems = new List<TItem>(query.Get<TEntity>().Rows.Select(row => CreateItem<TItem>(row)));
  133. if (ImageColumn != null)
  134. {
  135. Images.Clear();
  136. query.Get<Document>().IntoDictionary<Document, Guid, byte[]>(Images, x => x.ID,
  137. r => r.Get<Document, byte[]>(x => x.Data));
  138. }
  139. Search(null);
  140. AfterLoad(query);
  141. Loaded = true;
  142. loaded?.Invoke();
  143. }
  144. protected T CreateItem<T>(CoreRow row)
  145. where T : Shell<TParent,TEntity>, new()
  146. {
  147. var result = new T() { Row = row, Parent = (TParent)this };
  148. result.PropertyChanged += (sender, args) => DoPropertyChanged(result, args);
  149. return result;
  150. }
  151. IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
  152. {
  153. return Items.GetEnumerator();
  154. }
  155. public IEnumerator GetEnumerator()
  156. {
  157. return Items.GetEnumerator();
  158. }
  159. }
  160. }