DynamicDataGrid.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using System.Threading.Tasks;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using InABox.Clients;
  11. using InABox.Configuration;
  12. using InABox.Core;
  13. using InABox.WPF;
  14. using Expression = System.Linq.Expressions.Expression;
  15. namespace InABox.DynamicGrid
  16. {
  17. public interface IDynamicDataGrid : IDynamicGrid
  18. {
  19. /// <summary>
  20. /// The tag the the DynamicGridColumns are stored against. If set to <see langword="null"/>,
  21. /// the name of <typeparamref name="TEntity"/> is used as a default.
  22. /// </summary>
  23. string? ColumnsTag { get; set; }
  24. }
  25. public class DynamicDataGrid<TEntity> : DynamicGrid<TEntity>, IDynamicDataGrid where TEntity : Entity, IRemotable, IPersistent, new()
  26. {
  27. public delegate void OnReloadEventHandler(object sender, Filters<TEntity> criteria, Columns<TEntity> columns, ref SortOrder<TEntity>? sortby);
  28. private readonly int ChunkSize = 500;
  29. private readonly Button MergeBtn;
  30. private readonly Button FilterBtn;
  31. private bool _showFilterList;
  32. public bool ShowFilterList {
  33. get => _showFilterList;
  34. set
  35. {
  36. _showFilterList = value;
  37. if(FilterBtn != null) // FilterBtn can be null when ShowFilterList is set in DynamicGrid constructor
  38. FilterBtn.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
  39. }
  40. }
  41. private Tuple<string, Filter<TEntity>>? Filter;
  42. public DynamicDataGrid() : base()
  43. {
  44. FilterBtn = AddButton("", Properties.Resources.filter.AsBitmapImage(), DoFilter);
  45. FilterBtn.Margin = new Thickness(0, 2, 7, 0);
  46. FilterBtn.Padding = new Thickness(0);
  47. FilterBtn.Visibility = ShowFilterList ? Visibility.Visible : Visibility.Collapsed;
  48. Options.BeginUpdate();
  49. if (Security.CanEdit<TEntity>())
  50. Options.Add(DynamicGridOption.AddRows).Add(DynamicGridOption.EditRows);
  51. if (Security.CanDelete<TEntity>())
  52. Options.Add(DynamicGridOption.DeleteRows);
  53. if (Security.CanImport<TEntity>())
  54. Options.Add(DynamicGridOption.ImportData);
  55. if (Security.CanExport<TEntity>())
  56. Options.Add(DynamicGridOption.ExportData);
  57. if (Security.CanMerge<TEntity>())
  58. Options.Add(DynamicGridOption.MultiSelect);
  59. Options.EndUpdate();
  60. MergeBtn = AddButton("Merge", Properties.Resources.merge.AsBitmapImage(Color.White), DoMerge);
  61. var fields = DatabaseSchema.Properties(typeof(TEntity));
  62. foreach (var field in fields)
  63. if (!MasterColumns.Any(x => x.ColumnName == field.Name))
  64. MasterColumns.Add(new DynamicGridColumn { ColumnName = field.Name });
  65. var cols = LookupFactory.DefineColumns<TEntity>();
  66. // Minimum Columns for Lookup values
  67. foreach (var col in cols.Items)
  68. HiddenColumns.Add(CoreUtils.CreateLambdaExpression<TEntity>(col.Property));
  69. // Minimum Columns for Successful Saving
  70. // This should be cross-checked with the relevant Store<>
  71. // so that clients will (usually) provide sufficient columns for saving
  72. var required = LookupFactory.RequiredColumns<TEntity>();
  73. foreach (var col in required.Items)
  74. HiddenColumns.Add(CoreUtils.CreateLambdaExpression<TEntity>(col.Property));
  75. //HiddenColumns.Add(x => x.ID);
  76. if (typeof(TEntity).GetInterfaces().Contains(typeof(IIssues)))
  77. {
  78. HiddenColumns.Add(x => (x as IIssues)!.Issues);
  79. var coltype = typeof(DynamicIssuesColumn<>).MakeGenericType(typeof(TEntity));
  80. ActionColumns.Add((Activator.CreateInstance(coltype, this) as DynamicActionColumn)!);
  81. }
  82. }
  83. protected override void CustomiseEditorForm(DynamicEditorForm form)
  84. {
  85. base.CustomiseEditorForm(form);
  86. form.ReadOnly = !Security.CanEdit<TEntity>();
  87. }
  88. public string? ColumnsTag { get; set; }
  89. protected override void OptionsChanged(object sender, EventArgs args)
  90. {
  91. base.OptionsChanged(sender, args);
  92. if (MergeBtn != null)
  93. MergeBtn.Visibility = Visibility.Collapsed;
  94. ShowFilterList = Options.Contains(DynamicGridOption.FilterRows);
  95. }
  96. protected override void SelectItems(CoreRow[]? rows)
  97. {
  98. base.SelectItems(rows);
  99. MergeBtn.Visibility = Options.Contains(DynamicGridOption.MultiSelect) && Security.CanMerge<TEntity>() && rows != null && rows.Length > 1
  100. ? Visibility.Visible
  101. : Visibility.Collapsed;
  102. }
  103. private Filter<T>? CreateFilter<T>(Dictionary<string, object> criteria) where T : Entity
  104. {
  105. Filter<T>? filter = null;
  106. foreach (var key in criteria.Keys)
  107. {
  108. var exp = CoreUtils.GetPropertyExpression<T>(key);
  109. var flt = new Filter<T>(exp).IsEqualTo(criteria[key]);
  110. if (filter != null)
  111. filter.Ands.Add(flt);
  112. else
  113. filter = flt;
  114. }
  115. return filter;
  116. }
  117. private Columns<T> CreateColumns<T>(List<string> columnnames) where T : Entity
  118. {
  119. var expressions = new List<Expression<Func<T, object?>>>();
  120. foreach (var columnname in columnnames)
  121. try
  122. {
  123. var expression = CoreUtils.GetPropertyExpression<T>(columnname);
  124. expressions.Add(expression);
  125. }
  126. catch (Exception e)
  127. {
  128. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  129. }
  130. if (!columnnames.Contains("ID"))
  131. expressions.Add(CoreUtils.GetPropertyExpression<T>("ID"));
  132. //if (!columnnames.Contains("Attributes"))
  133. // expressions.Add(CoreUtils.GetPropertyExpression<T>("Attributes"));
  134. return new Columns<T>(expressions.ToArray());
  135. }
  136. private SortOrder<T>? CreateSortOrder<T>(string columnname) where T : Entity
  137. {
  138. if (string.IsNullOrWhiteSpace(columnname))
  139. return null;
  140. var expression = CoreUtils.GetPropertyExpression<T>(columnname);
  141. return new SortOrder<T>(expression);
  142. }
  143. public event OnReloadEventHandler? OnReload;
  144. protected override void Reload(Filters<TEntity> criteria, Columns<TEntity> columns, ref SortOrder<TEntity>? sort,
  145. Action<CoreTable?, Exception?> action)
  146. {
  147. //if (sort == null)
  148. // sort = new SortOrder<TEntity>(x => x.Sort);
  149. if (Filter != null)
  150. criteria.Add(Filter.Item2);
  151. OnReload?.Invoke(this, criteria, columns, ref sort);
  152. new Client<TEntity>().Query(criteria.Combine(), columns, sort, action);
  153. }
  154. public override void Refresh(bool reloadcolumns, bool reloaddata)
  155. {
  156. var selectedids = SelectedRows.Select(x => x.Get<TEntity, Guid>(c => c.ID)).ToArray();
  157. base.Refresh(reloadcolumns, reloaddata);
  158. SelectedRows = Data.Rows.Where(r => selectedids.Contains(r.Get<TEntity, Guid>(c => c.ID))).ToArray();
  159. SelectItems(SelectedRows);
  160. }
  161. public Columns<TEntity> LoadEditorColumns()
  162. {
  163. var result = new Columns<TEntity>().Default(
  164. ColumnType.IncludeOptional,
  165. ColumnType.IncludeForeignKeys,
  166. ColumnType.IncludeUserProperties,
  167. ColumnType.IncludeEditable);
  168. var cols = DataColumns();
  169. foreach (var col in cols.Items)
  170. if (!result.Items.Any(x => string.Equals(x.Property, col.Property)))
  171. result.Add(col.Property);
  172. var props = DatabaseSchema.Properties(typeof(TEntity))
  173. .Where(x => x.Setter() != null)
  174. .OrderBy(x => CoreUtils.GetPropertySequence(typeof(TEntity), x.Name));
  175. foreach(var col in result.Items)
  176. {
  177. var prop = DatabaseSchema.Property(typeof(TEntity), col.Property);
  178. if(prop?.Editor is DataLookupEditor dataLookup)
  179. {
  180. foreach(var lookupColumn in LookupFactory.DefineFilterColumns<TEntity>(dataLookup.Type).ColumnNames())
  181. {
  182. result.Add(lookupColumn);
  183. }
  184. }
  185. }
  186. return result;
  187. }
  188. protected override TEntity[] LoadItems(CoreRow[] rows)
  189. {
  190. Filter<TEntity>? filter = null;
  191. var results = new List<TEntity>();
  192. for (var i = 0; i < rows.Length; i = i + ChunkSize)
  193. {
  194. var chunk = rows.Skip(i).Take(ChunkSize);
  195. foreach (var row in chunk)
  196. {
  197. var id = row.Get<TEntity, Guid>(x => x.ID);
  198. if (filter is null)
  199. filter = new Filter<TEntity>(x => x.ID).IsEqualTo(id);
  200. else
  201. filter = filter.Or(x => x.ID).IsEqualTo(id);
  202. }
  203. var columns = LoadEditorColumns(); // new Columns<TEntity>().Default(ColumnType.IncludeOptional, ColumnType.IncludeForeignKeys, ColumnType.IncludeUserProperties);
  204. var data = new Client<TEntity>().Query(filter, columns);
  205. results.AddRange(data.Rows.Select(x => x.ToObject<TEntity>()));
  206. }
  207. return results.ToArray();
  208. }
  209. protected override TEntity LoadItem(CoreRow row)
  210. {
  211. var id = row.Get<TEntity, Guid>(x => x.ID);
  212. var filter = new Filter<TEntity>(x => x.ID).IsEqualTo(id);
  213. return new Client<TEntity>().Load(filter).FirstOrDefault()
  214. ?? throw new Exception($"No {typeof(TEntity).Name} with ID {id}");
  215. }
  216. protected override void SaveItem(TEntity item)
  217. {
  218. new Client<TEntity>().Save(item, "Edited by User");
  219. }
  220. protected override void SaveItems(TEntity[] items)
  221. {
  222. new Client<TEntity>().Save(items, "Edited by User");
  223. }
  224. protected override void DeleteItems(params CoreRow[] rows)
  225. {
  226. var deletes = new List<TEntity>();
  227. foreach (var row in rows)
  228. {
  229. var delete = /* row.ToObject<TEntity>(); */ new TEntity { ID = row.Get<TEntity, Guid>(x => x.ID) };
  230. deletes.Add(delete);
  231. }
  232. new Client<TEntity>().Delete(deletes, "Deleted on User Request");
  233. }
  234. private object GetPropertyValue(Expression member, object obj)
  235. {
  236. var objectMember = Expression.Convert(member, typeof(object));
  237. var getterLambda = Expression.Lambda<Func<object>>(objectMember);
  238. var getter = getterLambda.Compile();
  239. return getter();
  240. }
  241. protected override void ObjectToRow(TEntity obj, CoreRow row)
  242. {
  243. foreach (var column in Data.Columns)
  244. {
  245. var prop = DatabaseSchema.Property(obj.GetType(), column.ColumnName);
  246. if (prop is StandardProperty)
  247. row.Set(column.ColumnName, CoreUtils.GetPropertyValue(obj, prop.Name));
  248. else if (prop is CustomProperty)
  249. row.Set(column.ColumnName, obj.UserProperties[column.ColumnName]);
  250. }
  251. //base.ObjectToRow(obj, row);
  252. }
  253. //private void Auditgrid_OnReload(object sender, Dictionary<string, object> criteria, ref String sort)
  254. //{
  255. // criteria["DocumentType"] = typeof(TEntity).EntityName();
  256. // criteria["DocumentID"] = (sender as FrameworkElement).Tag;
  257. // sort = "TimeStamp";
  258. //}
  259. public override void LoadEditorButtons(TEntity item, DynamicEditorButtons buttons)
  260. {
  261. base.LoadEditorButtons(item, buttons);
  262. if (ClientFactory.IsSupported<AuditTrail>())
  263. buttons.Add("Audit Trail", Properties.Resources.view.AsBitmapImage(), item, AuditTrailClick);
  264. }
  265. private void AuditTrailClick(object sender, object item)
  266. {
  267. var entity = (TEntity)item;
  268. var window = new AuditWindow(entity.ID);
  269. window.ShowDialog();
  270. }
  271. protected override void LoadColumnsMenu(ContextMenu menu)
  272. {
  273. base.LoadColumnsMenu(menu);
  274. //menu.Items.Add(new Separator());
  275. var ResetColumns = new MenuItem { Header = "Reset Columns to Default" };
  276. ResetColumns.Click += ResetColumnsClick;
  277. menu.Items.Add(ResetColumns);
  278. if (Security.IsAllowed<CanSetDefaultColumns>())
  279. {
  280. menu.Items.Add(new Separator());
  281. var UpdateDefaultColumns = new MenuItem { Header = "Mark Columns as Default" };
  282. UpdateDefaultColumns.Click += UpdateDefaultColumnsClick;
  283. menu.Items.Add(UpdateDefaultColumns);
  284. }
  285. }
  286. private void ResetColumnsClick(object sender, RoutedEventArgs e)
  287. {
  288. VisibleColumns.Clear();
  289. SaveColumns(VisibleColumns);
  290. Refresh(true, true);
  291. }
  292. private void UpdateDefaultColumnsClick(object sender, RoutedEventArgs e)
  293. {
  294. var tag = GetTag();
  295. new GlobalConfiguration<DynamicGridColumns>(tag).Save(VisibleColumns);
  296. new UserConfiguration<DynamicGridColumns>(tag).Delete();
  297. Refresh(true, true);
  298. }
  299. private string GetTag()
  300. {
  301. var tag = typeof(TEntity).Name;
  302. if (!string.IsNullOrWhiteSpace(ColumnsTag))
  303. tag = string.Format("{0}.{1}", tag, ColumnsTag);
  304. return tag;
  305. }
  306. private BaseEditor GetColumnEditor(DynamicGridColumn column)
  307. {
  308. try
  309. {
  310. var prop = DatabaseSchema.Property(typeof(TEntity), column.ColumnName);
  311. if (prop != null) return prop.Editor;
  312. var type = CoreUtils.GetProperty(typeof(TEntity), column.ColumnName);
  313. return type.GetEditor() ?? new NullEditor();
  314. }
  315. catch (Exception e)
  316. {
  317. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  318. }
  319. return new NullEditor();
  320. }
  321. protected override DynamicGridColumns LoadColumns()
  322. {
  323. var tag = GetTag();
  324. var user = Task.Run(() => new UserConfiguration<DynamicGridColumns>(tag).Load());
  325. var global = Task.Run(() => new GlobalConfiguration<DynamicGridColumns>(tag).Load());
  326. Task.WaitAll(user, global);
  327. var columns = user.Result.Any() ? user.Result : global.Result;
  328. if (!columns.Any())
  329. GenerateColumns(columns); //override this to provide specific columns on startup
  330. var removes = columns.Where(x => DatabaseSchema.Property(typeof(TEntity), x.ColumnName) == null || GetColumnEditor(x) is NullEditor)
  331. .ToArray();
  332. foreach (var remove in removes)
  333. columns.Remove(remove);
  334. if (columns.Count == 0)
  335. columns.AddRange(base.LoadColumns());
  336. foreach (var column in columns)
  337. try
  338. {
  339. var prop = DatabaseSchema.Property(typeof(TEntity), column.ColumnName);
  340. if (prop != null)
  341. {
  342. column.Editor = prop.Editor;
  343. }
  344. else
  345. {
  346. var type = CoreUtils.GetProperty(typeof(TEntity), column.ColumnName);
  347. column.Editor = type.GetEditor() ?? new NullEditor();
  348. }
  349. }
  350. catch (Exception e)
  351. {
  352. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  353. }
  354. columns.RemoveAll(x => DatabaseSchema.Property(typeof(TEntity), x.ColumnName) == null || GetColumnEditor(x) is NullEditor);
  355. return columns;
  356. }
  357. protected virtual void GenerateColumns(DynamicGridColumns columns)
  358. {
  359. var cols = new Columns<TEntity>().Default(Options.Contains(DynamicGridOption.DirectEdit)
  360. ? new[] { ColumnType.IncludeForeignKeys }
  361. : new ColumnType[] { ColumnType.IncludeLinked, ColumnType.IncludeNestedLinks, ColumnType.IncludeFormulae, ColumnType.IncludeAggregates });
  362. if (cols != null)
  363. {
  364. foreach (var col in cols.Items)
  365. {
  366. var mc = MasterColumns.FirstOrDefault(x => x.ColumnName.Equals(col.Property));
  367. if (mc != null && !(mc.Editor is NullEditor) && mc.Editor.Visible != Visible.Hidden)
  368. columns.Add(mc);
  369. }
  370. }
  371. }
  372. protected override void SaveColumns(DynamicGridColumns columns)
  373. {
  374. var tag = GetTag();
  375. new UserConfiguration<DynamicGridColumns>(tag).Save(columns);
  376. }
  377. public override void ConfigureColumns(DynamicGridColumns columns /*, bool dolookups */)
  378. {
  379. //var eProps = CoreUtils.PropertyList(typeof(Entity), x => true, true);
  380. //foreach (String eProp in eProps.Keys)
  381. //{
  382. // var column = columns.Where(x => x.ColumnName.Equals(eProp)).FirstOrDefault();
  383. // if (column != null)
  384. // columns.Remove(column);
  385. //}
  386. base.ConfigureColumns(columns /*, dolookups */);
  387. }
  388. protected override BaseEditor? GetEditor(object item, DynamicGridColumn column)
  389. {
  390. var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == column.ColumnName);
  391. if (prop != null)
  392. return prop.Editor;
  393. return base.GetEditor(item, column);
  394. }
  395. protected override object? GetEditorValue(object item, string name)
  396. {
  397. if(item is TEntity entity)
  398. {
  399. var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == name);
  400. if (prop is CustomProperty)
  401. {
  402. if (entity.UserProperties.ContainsKey(name))
  403. return entity.UserProperties[name];
  404. return null;
  405. }
  406. }
  407. return base.GetEditorValue(item, name);
  408. }
  409. protected override void SetEditorValue(object item, string name, object value)
  410. {
  411. var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == name);
  412. if (prop is CustomProperty && item is TEntity entity)
  413. {
  414. entity.UserProperties[name] = value;
  415. }
  416. else
  417. {
  418. base.SetEditorValue(item, name, value);
  419. }
  420. }
  421. protected override Document? LoadDocument(Guid id)
  422. {
  423. return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
  424. }
  425. protected override Document? FindDocument(string filename)
  426. {
  427. return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
  428. }
  429. protected override void SaveDocument(Document document)
  430. {
  431. new Client<Document>().Save(document, "Updated by Editor");
  432. }
  433. protected bool Duplicate(
  434. CoreRow row,
  435. Expression<Func<TEntity, object?>> codefield,
  436. Type[] childtypes)
  437. {
  438. var id = row.Get<TEntity, Guid>(x => x.ID);
  439. var code = row.Get(codefield) as string;
  440. var tasks = new List<Task>();
  441. var itemtask = Task.Run(() =>
  442. {
  443. var filter = new Filter<TEntity>(x => x.ID).IsEqualTo(id);
  444. var result = new Client<TEntity>().Load(filter).FirstOrDefault()
  445. ?? throw new Exception("Entity does not exist!");
  446. return result;
  447. });
  448. tasks.Add(itemtask);
  449. //itemtask.Wait();
  450. Task<List<string?>>? codetask = null;
  451. if (!string.IsNullOrWhiteSpace(code))
  452. {
  453. codetask = Task.Run(() =>
  454. {
  455. var columns = new Columns<TEntity>(codefield);
  456. //columns.Add<String>(codefield);
  457. var filter = new Filter<TEntity>(codefield).BeginsWith(code);
  458. var table = new Client<TEntity>().Query(filter, columns);
  459. var result = table.Rows.Select(x => x.Get(codefield) as string).ToList();
  460. return result;
  461. });
  462. tasks.Add(codetask);
  463. }
  464. //codetask.Wait();
  465. var children = new Dictionary<Type, CoreTable>();
  466. foreach (var childtype in childtypes)
  467. {
  468. var childtask = Task.Run(() =>
  469. {
  470. var prop = childtype.GetProperties().FirstOrDefault(x =>
  471. x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) &&
  472. x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == typeof(TEntity));
  473. if(prop is not null)
  474. {
  475. var filter = Core.Filter.Create(childtype);
  476. filter.Expression = CoreUtils.GetMemberExpression(childtype, prop.Name + ".ID");
  477. filter.Operator = Operator.IsEqualTo;
  478. filter.Value = id;
  479. var sort = LookupFactory.DefineSort(childtype);
  480. var client = ClientFactory.CreateClient(childtype);
  481. var table = client.Query(filter, null, sort);
  482. foreach (var r in table.Rows)
  483. {
  484. r["ID"] = Guid.Empty;
  485. r[prop.Name + ".ID"] = Guid.Empty;
  486. }
  487. children[childtype] = table;
  488. }
  489. else
  490. {
  491. Logger.Send(LogType.Error, "", $"DynamicDataGrid<{typeof(TEntity)}>.Duplicate(): No parent property found for child type {childtype}");
  492. }
  493. });
  494. tasks.Add(childtask);
  495. //childtask.Wait();
  496. }
  497. //var manytomanys = CoreUtils.TypeList(
  498. // AppDomain.CurrentDomain.GetAssemblies(),
  499. // x => x.GetInterfaces().Any(intf => intf.IsGenericType && intf.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && intf.GenericTypeArguments.Contains(typeof(T)))
  500. //);
  501. //Task<CoreTable> childtask = Task.Run(() =>
  502. //{
  503. // var result = new CoreTable();
  504. // result.LoadColumns(typeof(TChild));
  505. // var children = new Client<TChild>().Load(new Filter<TChild>(x => linkfield).IsEqualTo(id));
  506. // foreach (var child in children)
  507. // {
  508. // child.ID = Guid.Empty;
  509. // String linkprop = CoreUtils.GetFullPropertyName<TChild, Guid>(linkfield, ".");
  510. // CoreUtils.SetPropertyValue(child, linkprop, Guid.Empty);
  511. // var newrow = result.NewRow();
  512. // result.LoadRow(newrow, child);
  513. // result.Rows.Add(newrow);
  514. // }
  515. // return result;
  516. //});
  517. //tasks.Add(childtask);
  518. Task.WaitAll(tasks.ToArray());
  519. var item = itemtask.Result;
  520. item.ID = Guid.Empty;
  521. if (codetask != null)
  522. {
  523. var codes = codetask.Result;
  524. var i = 1;
  525. while (codes.Contains(string.Format("{0} ({1})", code, i)))
  526. i++;
  527. var codeprop = CoreUtils.GetFullPropertyName(codefield, ".");
  528. CoreUtils.SetPropertyValue(item, codeprop, string.Format("{0} ({1})", code, i));
  529. }
  530. var grid = new DynamicDataGrid<TEntity>();
  531. return grid.EditItems(new[] { item }, t => children.ContainsKey(t) ? children[t] : null, true);
  532. }
  533. private bool DoFilter(Button button, CoreRow[] rows)
  534. {
  535. var menu = new ContextMenu();
  536. var none = new MenuItem { Header = "Clear Filters", Tag = null };
  537. none.Click += Filter_Click;
  538. menu.Items.Add(none);
  539. var tag = GetTag();
  540. var filters = new GlobalConfiguration<DynamicGridFilters>(tag).Load();
  541. foreach(var filter in filters)
  542. {
  543. var item = new MenuItem { Header = filter.Name, Tag = filter };
  544. if(Filter?.Item1 == filter.Name)
  545. {
  546. item.IsChecked = true;
  547. }
  548. item.Click += Filter_Click;
  549. menu.Items.Add(item);
  550. }
  551. if (Security.IsAllowed<CanCustomiseFilters>())
  552. {
  553. menu.Items.Add(new Separator());
  554. var manage = new MenuItem { Header = "Manage Filters" };
  555. manage.Click += (s, e) =>
  556. {
  557. var window = new DynamicGridFilterEditor(filters, typeof(TEntity));
  558. if(window.ShowDialog() == true)
  559. {
  560. new GlobalConfiguration<DynamicGridFilters>(tag).Save(filters);
  561. }
  562. };
  563. menu.Items.Add(manage);
  564. }
  565. menu.IsOpen = true;
  566. return false;
  567. }
  568. private void Filter_Click(object sender, RoutedEventArgs e)
  569. {
  570. var tag = (sender as MenuItem)?.Tag;
  571. Bitmap image;
  572. if(tag is DynamicGridFilter filter)
  573. {
  574. Filter = new(filter.Name, Serialization.Deserialize<Filter<TEntity>>(filter.Filter));
  575. image = Properties.Resources.filter_set;
  576. }
  577. else
  578. {
  579. Filter = null;
  580. image = Properties.Resources.filter;
  581. }
  582. UpdateButton(FilterBtn, image.AsBitmapImage(), "");
  583. Refresh(false, true);
  584. }
  585. private bool DoMerge(Button arg1, CoreRow[] arg2)
  586. {
  587. if (arg2 == null || arg2.Length <= 1)
  588. return false;
  589. var targetid = arg2.Last().Get<TEntity, Guid>(x => x.ID);
  590. var target = arg2.Last().ToObject<TEntity>().ToString();
  591. var otherids = arg2.Select(r => r.Get<TEntity, Guid>(x => x.ID)).Where(x => x != targetid).ToArray();
  592. string[] others = arg2.Where(r => otherids.Contains(r.Get<Guid>("ID"))).Select(x => x.ToObject<TEntity>().ToString()!).ToArray();
  593. var rows = arg2.Length;
  594. if (MessageBox.Show(
  595. string.Format(
  596. "This will merge the following items:\n\n- {0}\n\n into:\n\n- {1}\n\nAfter this, the items will be permanently removed.\nAre you sure you wish to do this?",
  597. string.Join("\n- ", others),
  598. target
  599. ),
  600. "Merge Items Warning",
  601. MessageBoxButton.YesNo,
  602. MessageBoxImage.Stop) != MessageBoxResult.Yes
  603. )
  604. return false;
  605. using (new WaitCursor())
  606. {
  607. var types = CoreUtils.TypeList(
  608. AppDomain.CurrentDomain.GetAssemblies(),
  609. x =>
  610. x.IsClass
  611. && !x.IsGenericType
  612. && x.IsSubclassOf(typeof(Entity))
  613. && !x.Equals(typeof(AuditTrail))
  614. && !x.Equals(typeof(TEntity))
  615. && x.GetCustomAttribute<AutoEntity>() == null
  616. ).ToArray();
  617. foreach (var type in types)
  618. {
  619. var props = CoreUtils.PropertyList(
  620. type,
  621. x =>
  622. x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink))
  623. && x.PropertyType.GetInheritedGenericTypeArguments().Contains(typeof(TEntity))
  624. );
  625. foreach (var prop in props)
  626. {
  627. var propname = string.Format(prop.Name + ".ID");
  628. var filter = Core.Filter.Create(type);
  629. filter.Expression = CoreUtils.CreateMemberExpression(type, propname);
  630. filter.Operator = Operator.InList;
  631. filter.Value = otherids;
  632. var columns = Columns.Create(type)
  633. .Add("ID")
  634. .Add(propname);
  635. var updates = ClientFactory.CreateClient(type).Query(filter, columns).Rows.Select(r => r.ToObject(type)).ToArray();
  636. if (updates.Any())
  637. {
  638. foreach (var update in updates)
  639. CoreUtils.SetPropertyValue(update, propname, targetid);
  640. ClientFactory.CreateClient(type).Save(updates,
  641. string.Format("Merged {0} Records", typeof(TEntity).EntityName().Split('.').Last()));
  642. }
  643. }
  644. }
  645. var histories = new Client<AuditTrail>()
  646. .Query(new Filter<AuditTrail>(x => x.EntityID).InList(otherids), new Columns<AuditTrail>(x => x.EntityID)).Rows
  647. .Select(x => x.ToObject<AuditTrail>()).ToArray();
  648. foreach (var history in histories)
  649. history.EntityID = targetid;
  650. if (histories.Any())
  651. new Client<AuditTrail>().Save(histories, "");
  652. var deletes = new List<object>();
  653. foreach (var otherid in otherids)
  654. {
  655. var delete = new TEntity();
  656. CoreUtils.SetPropertyValue(delete, "ID", otherid);
  657. deletes.Add(delete);
  658. }
  659. ClientFactory.CreateClient(typeof(TEntity))
  660. .Delete(deletes, string.Format("Merged {0} Records", typeof(TEntity).EntityName().Split('.').Last()));
  661. return true;
  662. }
  663. }
  664. protected override IEnumerable<TEntity> LoadDuplicatorItems(CoreRow[] rows)
  665. {
  666. return rows.Select(x => x.ToObject<TEntity>());
  667. }
  668. protected override bool BeforePaste(IEnumerable<TEntity> items, ClipAction action)
  669. {
  670. if (action == ClipAction.Copy)
  671. {
  672. foreach (var item in items)
  673. item.ID = Guid.Empty;
  674. return true;
  675. }
  676. return base.BeforePaste(items, action);
  677. }
  678. protected override void CustomiseExportFilters(Filters<TEntity> filters, CoreRow[] visiblerows)
  679. {
  680. base.CustomiseExportFilters(filters, visiblerows);
  681. var ids = visiblerows.Select(r => r.Get<TEntity, Guid>(c => c.ID)).ToArray();
  682. filters.Add(new Filter<TEntity>(x => x.ID).InList(ids));
  683. }
  684. protected override IEnumerable<Tuple<Type?, CoreTable>> LoadExportTables(Filters<TEntity> filter, IEnumerable<Tuple<Type, IColumns>> tableColumns)
  685. {
  686. var queries = new Dictionary<string, IQueryDef>();
  687. var columns = tableColumns.ToList();
  688. foreach (var table in columns)
  689. {
  690. var tableType = table.Item1;
  691. PropertyInfo? property = null;
  692. var m2m = CoreUtils.GetManyToMany(tableType, typeof(TEntity));
  693. IFilter? queryFilter = null;
  694. if (m2m != null)
  695. {
  696. property = CoreUtils.GetManyToManyThisProperty(tableType, typeof(TEntity));
  697. }
  698. else
  699. {
  700. var o2m = CoreUtils.GetOneToMany(tableType, typeof(TEntity));
  701. if (o2m != null)
  702. {
  703. property = CoreUtils.GetOneToManyProperty(tableType, typeof(TEntity));
  704. }
  705. }
  706. if (property != null)
  707. {
  708. var subQuery = new SubQuery<TEntity>();
  709. subQuery.Filter = filter.Combine();
  710. subQuery.Column = new Column<TEntity>(x => x.ID);
  711. queryFilter = (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(tableType)) as IFilter)!;
  712. queryFilter.Expression = CoreUtils.GetMemberExpression(tableType, property.Name + ".ID");
  713. queryFilter.InQuery(subQuery);
  714. queries[tableType.Name] = new QueryDef(tableType)
  715. {
  716. Filter = queryFilter,
  717. Columns = table.Item2
  718. };
  719. }
  720. }
  721. var results = Client.QueryMultiple(queries);
  722. return columns.Select(x => new Tuple<Type?, CoreTable>(x.Item1, results[x.Item1.Name]));
  723. }
  724. }
  725. }