DynamicDataGrid.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  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. return result;
  173. }
  174. protected override TEntity[] LoadItems(CoreRow[] rows)
  175. {
  176. Filter<TEntity>? filter = null;
  177. var results = new List<TEntity>();
  178. for (var i = 0; i < rows.Length; i = i + ChunkSize)
  179. {
  180. var chunk = rows.Skip(i).Take(ChunkSize);
  181. foreach (var row in chunk)
  182. {
  183. var id = row.Get<TEntity, Guid>(x => x.ID);
  184. if (filter is null)
  185. filter = new Filter<TEntity>(x => x.ID).IsEqualTo(id);
  186. else
  187. filter = filter.Or(x => x.ID).IsEqualTo(id);
  188. }
  189. var columns = LoadEditorColumns(); // new Columns<TEntity>().Default(ColumnType.IncludeOptional, ColumnType.IncludeForeignKeys, ColumnType.IncludeUserProperties);
  190. var data = new Client<TEntity>().Query(filter, columns);
  191. results.AddRange(data.Rows.Select(x => x.ToObject<TEntity>()));
  192. }
  193. return results.ToArray();
  194. }
  195. protected override TEntity LoadItem(CoreRow row)
  196. {
  197. var id = row.Get<TEntity, Guid>(x => x.ID);
  198. var filter = new Filter<TEntity>(x => x.ID).IsEqualTo(id);
  199. return new Client<TEntity>().Load(filter).FirstOrDefault()
  200. ?? throw new Exception($"No {typeof(TEntity).Name} with ID {id}");
  201. }
  202. protected override void SaveItem(TEntity item)
  203. {
  204. new Client<TEntity>().Save(item, "Edited by User");
  205. }
  206. protected override void SaveItems(TEntity[] items)
  207. {
  208. new Client<TEntity>().Save(items, "Edited by User");
  209. }
  210. protected override void DeleteItems(params CoreRow[] rows)
  211. {
  212. var deletes = new List<TEntity>();
  213. foreach (var row in rows)
  214. {
  215. var delete = /* row.ToObject<TEntity>(); */ new TEntity { ID = row.Get<TEntity, Guid>(x => x.ID) };
  216. deletes.Add(delete);
  217. }
  218. new Client<TEntity>().Delete(deletes, "Deleted on User Request");
  219. }
  220. private object GetPropertyValue(Expression member, object obj)
  221. {
  222. var objectMember = Expression.Convert(member, typeof(object));
  223. var getterLambda = Expression.Lambda<Func<object>>(objectMember);
  224. var getter = getterLambda.Compile();
  225. return getter();
  226. }
  227. protected override void ObjectToRow(TEntity obj, CoreRow row)
  228. {
  229. foreach (var column in Data.Columns)
  230. {
  231. var prop = DatabaseSchema.Property(obj.GetType(), column.ColumnName);
  232. if (prop is StandardProperty)
  233. row.Set(column.ColumnName, CoreUtils.GetPropertyValue(obj, prop.Name));
  234. else if (prop is CustomProperty)
  235. row.Set(column.ColumnName, obj.UserProperties[column.ColumnName]);
  236. }
  237. //base.ObjectToRow(obj, row);
  238. }
  239. //private void Auditgrid_OnReload(object sender, Dictionary<string, object> criteria, ref String sort)
  240. //{
  241. // criteria["DocumentType"] = typeof(TEntity).EntityName();
  242. // criteria["DocumentID"] = (sender as FrameworkElement).Tag;
  243. // sort = "TimeStamp";
  244. //}
  245. public override void LoadEditorButtons(TEntity item, DynamicEditorButtons buttons)
  246. {
  247. base.LoadEditorButtons(item, buttons);
  248. if (ClientFactory.IsSupported<AuditTrail>())
  249. buttons.Add("Audit Trail", Properties.Resources.view.AsBitmapImage(), item, AuditTrailClick);
  250. }
  251. private void AuditTrailClick(object sender, object item)
  252. {
  253. var entity = (TEntity)item;
  254. var window = new AuditWindow(entity.ID);
  255. window.ShowDialog();
  256. }
  257. protected override void LoadColumnsMenu(ContextMenu menu)
  258. {
  259. base.LoadColumnsMenu(menu);
  260. //menu.Items.Add(new Separator());
  261. var ResetColumns = new MenuItem { Header = "Reset Columns to Default" };
  262. ResetColumns.Click += ResetColumnsClick;
  263. menu.Items.Add(ResetColumns);
  264. if (Security.IsAllowed<CanSetDefaultColumns>())
  265. {
  266. menu.Items.Add(new Separator());
  267. var UpdateDefaultColumns = new MenuItem { Header = "Mark Columns as Default" };
  268. UpdateDefaultColumns.Click += UpdateDefaultColumnsClick;
  269. menu.Items.Add(UpdateDefaultColumns);
  270. }
  271. }
  272. private void ResetColumnsClick(object sender, RoutedEventArgs e)
  273. {
  274. VisibleColumns.Clear();
  275. SaveColumns(VisibleColumns);
  276. Refresh(true, true);
  277. }
  278. private void UpdateDefaultColumnsClick(object sender, RoutedEventArgs e)
  279. {
  280. var tag = GetTag();
  281. new GlobalConfiguration<DynamicGridColumns>(tag).Save(VisibleColumns);
  282. new UserConfiguration<DynamicGridColumns>(tag).Delete();
  283. Refresh(true, true);
  284. }
  285. private string GetTag()
  286. {
  287. var tag = typeof(TEntity).Name;
  288. if (!string.IsNullOrWhiteSpace(ColumnsTag))
  289. tag = string.Format("{0}.{1}", tag, ColumnsTag);
  290. return tag;
  291. }
  292. private BaseEditor GetColumnEditor(DynamicGridColumn column)
  293. {
  294. try
  295. {
  296. var prop = DatabaseSchema.Property(typeof(TEntity), column.ColumnName);
  297. if (prop != null) return prop.Editor;
  298. var type = CoreUtils.GetProperty(typeof(TEntity), column.ColumnName);
  299. return type.GetEditor() ?? new NullEditor();
  300. }
  301. catch (Exception e)
  302. {
  303. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  304. }
  305. return new NullEditor();
  306. }
  307. protected override DynamicGridColumns LoadColumns()
  308. {
  309. var tag = GetTag();
  310. var user = Task.Run(() => new UserConfiguration<DynamicGridColumns>(tag).Load());
  311. var global = Task.Run(() => new GlobalConfiguration<DynamicGridColumns>(tag).Load());
  312. Task.WaitAll(user, global);
  313. var columns = user.Result.Any() ? user.Result : global.Result;
  314. if (!columns.Any())
  315. GenerateColumns(columns); //override this to provide specific columns on startup
  316. var removes = columns.Where(x => DatabaseSchema.Property(typeof(TEntity), x.ColumnName) == null || GetColumnEditor(x) is NullEditor)
  317. .ToArray();
  318. foreach (var remove in removes)
  319. columns.Remove(remove);
  320. if (columns.Count == 0)
  321. columns.AddRange(base.LoadColumns());
  322. foreach (var column in columns)
  323. try
  324. {
  325. var prop = DatabaseSchema.Property(typeof(TEntity), column.ColumnName);
  326. if (prop != null)
  327. {
  328. column.Editor = prop.Editor;
  329. }
  330. else
  331. {
  332. var type = CoreUtils.GetProperty(typeof(TEntity), column.ColumnName);
  333. column.Editor = type.GetEditor() ?? new NullEditor();
  334. }
  335. }
  336. catch (Exception e)
  337. {
  338. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  339. }
  340. columns.RemoveAll(x => DatabaseSchema.Property(typeof(TEntity), x.ColumnName) == null || GetColumnEditor(x) is NullEditor);
  341. return columns;
  342. }
  343. protected virtual void GenerateColumns(DynamicGridColumns columns)
  344. {
  345. var cols = new Columns<TEntity>().Default(Options.Contains(DynamicGridOption.DirectEdit)
  346. ? new[] { ColumnType.IncludeForeignKeys }
  347. : new ColumnType[] { ColumnType.IncludeLinked, ColumnType.IncludeNestedLinks, ColumnType.IncludeFormulae, ColumnType.IncludeAggregates });
  348. if (cols != null)
  349. {
  350. foreach (var col in cols.Items)
  351. {
  352. var mc = MasterColumns.FirstOrDefault(x => x.ColumnName.Equals(col.Property));
  353. if (mc != null && !(mc.Editor is NullEditor) && mc.Editor.Visible != Visible.Hidden)
  354. columns.Add(mc);
  355. }
  356. }
  357. }
  358. protected override void SaveColumns(DynamicGridColumns columns)
  359. {
  360. var tag = GetTag();
  361. new UserConfiguration<DynamicGridColumns>(tag).Save(columns);
  362. }
  363. public override void ConfigureColumns(DynamicGridColumns columns /*, bool dolookups */)
  364. {
  365. //var eProps = CoreUtils.PropertyList(typeof(Entity), x => true, true);
  366. //foreach (String eProp in eProps.Keys)
  367. //{
  368. // var column = columns.Where(x => x.ColumnName.Equals(eProp)).FirstOrDefault();
  369. // if (column != null)
  370. // columns.Remove(column);
  371. //}
  372. base.ConfigureColumns(columns /*, dolookups */);
  373. }
  374. protected override BaseEditor? GetEditor(object item, DynamicGridColumn column)
  375. {
  376. var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == column.ColumnName);
  377. if (prop != null)
  378. return prop.Editor;
  379. return base.GetEditor(item, column);
  380. }
  381. protected override object? GetEditorValue(object item, string name)
  382. {
  383. if(item is TEntity entity)
  384. {
  385. var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == name);
  386. if (prop is CustomProperty)
  387. {
  388. if (entity.UserProperties.ContainsKey(name))
  389. return entity.UserProperties[name];
  390. return null;
  391. }
  392. }
  393. return base.GetEditorValue(item, name);
  394. }
  395. protected override void SetEditorValue(object item, string name, object value)
  396. {
  397. var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == name);
  398. if (prop is CustomProperty && item is TEntity entity)
  399. {
  400. entity.UserProperties[name] = value;
  401. }
  402. else
  403. {
  404. base.SetEditorValue(item, name, value);
  405. }
  406. }
  407. protected override Document? LoadDocument(Guid id)
  408. {
  409. return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
  410. }
  411. protected override Document? FindDocument(string filename)
  412. {
  413. return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
  414. }
  415. protected override void SaveDocument(Document document)
  416. {
  417. new Client<Document>().Save(document, "Updated by Editor");
  418. }
  419. protected bool Duplicate(
  420. CoreRow row,
  421. Expression<Func<TEntity, object?>> codefield,
  422. Type[] childtypes)
  423. {
  424. var id = row.Get<TEntity, Guid>(x => x.ID);
  425. var code = row.Get(codefield) as string;
  426. var tasks = new List<Task>();
  427. var itemtask = Task.Run(() =>
  428. {
  429. var filter = new Filter<TEntity>(x => x.ID).IsEqualTo(id);
  430. var result = new Client<TEntity>().Load(filter).FirstOrDefault()
  431. ?? throw new Exception("Entity does not exist!");
  432. return result;
  433. });
  434. tasks.Add(itemtask);
  435. //itemtask.Wait();
  436. Task<List<string?>>? codetask = null;
  437. if (!string.IsNullOrWhiteSpace(code))
  438. {
  439. codetask = Task.Run(() =>
  440. {
  441. var columns = new Columns<TEntity>(codefield);
  442. //columns.Add<String>(codefield);
  443. var filter = new Filter<TEntity>(codefield).BeginsWith(code);
  444. var table = new Client<TEntity>().Query(filter, columns);
  445. var result = table.Rows.Select(x => x.Get(codefield) as string).ToList();
  446. return result;
  447. });
  448. tasks.Add(codetask);
  449. }
  450. //codetask.Wait();
  451. var children = new Dictionary<Type, CoreTable>();
  452. foreach (var childtype in childtypes)
  453. {
  454. var childtask = Task.Run(() =>
  455. {
  456. var prop = childtype.GetProperties().FirstOrDefault(x =>
  457. x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) &&
  458. x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == typeof(TEntity));
  459. if(prop is not null)
  460. {
  461. var filter = Core.Filter.Create(childtype);
  462. filter.Expression = CoreUtils.GetMemberExpression(childtype, prop.Name + ".ID");
  463. filter.Operator = Operator.IsEqualTo;
  464. filter.Value = id;
  465. var sort = LookupFactory.DefineSort(childtype);
  466. var client = ClientFactory.CreateClient(childtype);
  467. var table = client.Query(filter, null, sort);
  468. foreach (var r in table.Rows)
  469. {
  470. r["ID"] = Guid.Empty;
  471. r[prop.Name + ".ID"] = Guid.Empty;
  472. }
  473. children[childtype] = table;
  474. }
  475. else
  476. {
  477. Logger.Send(LogType.Error, "", $"DynamicDataGrid<{typeof(TEntity)}>.Duplicate(): No parent property found for child type {childtype}");
  478. }
  479. });
  480. tasks.Add(childtask);
  481. //childtask.Wait();
  482. }
  483. //var manytomanys = CoreUtils.TypeList(
  484. // AppDomain.CurrentDomain.GetAssemblies(),
  485. // x => x.GetInterfaces().Any(intf => intf.IsGenericType && intf.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && intf.GenericTypeArguments.Contains(typeof(T)))
  486. //);
  487. //Task<CoreTable> childtask = Task.Run(() =>
  488. //{
  489. // var result = new CoreTable();
  490. // result.LoadColumns(typeof(TChild));
  491. // var children = new Client<TChild>().Load(new Filter<TChild>(x => linkfield).IsEqualTo(id));
  492. // foreach (var child in children)
  493. // {
  494. // child.ID = Guid.Empty;
  495. // String linkprop = CoreUtils.GetFullPropertyName<TChild, Guid>(linkfield, ".");
  496. // CoreUtils.SetPropertyValue(child, linkprop, Guid.Empty);
  497. // var newrow = result.NewRow();
  498. // result.LoadRow(newrow, child);
  499. // result.Rows.Add(newrow);
  500. // }
  501. // return result;
  502. //});
  503. //tasks.Add(childtask);
  504. Task.WaitAll(tasks.ToArray());
  505. var item = itemtask.Result;
  506. item.ID = Guid.Empty;
  507. if (codetask != null)
  508. {
  509. var codes = codetask.Result;
  510. var i = 1;
  511. while (codes.Contains(string.Format("{0} ({1})", code, i)))
  512. i++;
  513. var codeprop = CoreUtils.GetFullPropertyName(codefield, ".");
  514. CoreUtils.SetPropertyValue(item, codeprop, string.Format("{0} ({1})", code, i));
  515. }
  516. var grid = new DynamicDataGrid<TEntity>();
  517. return grid.EditItems(new[] { item }, t => children.ContainsKey(t) ? children[t] : null, true);
  518. }
  519. private bool DoFilter(Button button, CoreRow[] rows)
  520. {
  521. var menu = new ContextMenu();
  522. var none = new MenuItem { Header = "Clear Filters", Tag = null };
  523. none.Click += Filter_Click;
  524. menu.Items.Add(none);
  525. var tag = GetTag();
  526. var filters = new GlobalConfiguration<DynamicGridFilters>(tag).Load();
  527. foreach(var filter in filters)
  528. {
  529. var item = new MenuItem { Header = filter.Name, Tag = filter };
  530. if(Filter?.Item1 == filter.Name)
  531. {
  532. item.IsChecked = true;
  533. }
  534. item.Click += Filter_Click;
  535. menu.Items.Add(item);
  536. }
  537. if (Security.IsAllowed<CanCustomiseFilters>())
  538. {
  539. menu.Items.Add(new Separator());
  540. var manage = new MenuItem { Header = "Manage Filters" };
  541. manage.Click += (s, e) =>
  542. {
  543. var window = new DynamicGridFilterEditor(filters, typeof(TEntity));
  544. if(window.ShowDialog() == true)
  545. {
  546. new GlobalConfiguration<DynamicGridFilters>(tag).Save(filters);
  547. }
  548. };
  549. menu.Items.Add(manage);
  550. }
  551. menu.IsOpen = true;
  552. return false;
  553. }
  554. private void Filter_Click(object sender, RoutedEventArgs e)
  555. {
  556. var tag = (sender as MenuItem)?.Tag;
  557. Bitmap image;
  558. if(tag is DynamicGridFilter filter)
  559. {
  560. Filter = new(filter.Name, Serialization.Deserialize<Filter<TEntity>>(filter.Filter));
  561. image = Properties.Resources.filter_set;
  562. }
  563. else
  564. {
  565. Filter = null;
  566. image = Properties.Resources.filter;
  567. }
  568. UpdateButton(FilterBtn, image.AsBitmapImage(), "");
  569. Refresh(false, true);
  570. }
  571. private bool DoMerge(Button arg1, CoreRow[] arg2)
  572. {
  573. if (arg2 == null || arg2.Length <= 1)
  574. return false;
  575. var targetid = arg2.Last().Get<TEntity, Guid>(x => x.ID);
  576. var target = arg2.Last().ToObject<TEntity>().ToString();
  577. var otherids = arg2.Select(r => r.Get<TEntity, Guid>(x => x.ID)).Where(x => x != targetid).ToArray();
  578. string[] others = arg2.Where(r => otherids.Contains(r.Get<Guid>("ID"))).Select(x => x.ToObject<TEntity>().ToString()!).ToArray();
  579. var rows = arg2.Length;
  580. if (MessageBox.Show(
  581. string.Format(
  582. "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?",
  583. string.Join("\n- ", others),
  584. target
  585. ),
  586. "Merge Items Warning",
  587. MessageBoxButton.YesNo,
  588. MessageBoxImage.Stop) != MessageBoxResult.Yes
  589. )
  590. return false;
  591. using (new WaitCursor())
  592. {
  593. var types = CoreUtils.TypeList(
  594. AppDomain.CurrentDomain.GetAssemblies(),
  595. x =>
  596. x.IsClass
  597. && !x.IsGenericType
  598. && x.IsSubclassOf(typeof(Entity))
  599. && !x.Equals(typeof(AuditTrail))
  600. && !x.Equals(typeof(TEntity))
  601. && x.GetCustomAttribute<AutoEntity>() == null
  602. ).ToArray();
  603. foreach (var type in types)
  604. {
  605. var props = CoreUtils.PropertyList(
  606. type,
  607. x =>
  608. x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink))
  609. && x.PropertyType.GetInheritedGenericTypeArguments().Contains(typeof(TEntity))
  610. );
  611. foreach (var prop in props)
  612. {
  613. var propname = string.Format(prop.Name + ".ID");
  614. var filter = Core.Filter.Create(type);
  615. filter.Expression = CoreUtils.CreateMemberExpression(type, propname);
  616. filter.Operator = Operator.InList;
  617. filter.Value = otherids;
  618. var columns = Columns.Create(type)
  619. .Add("ID")
  620. .Add(propname);
  621. var updates = ClientFactory.CreateClient(type).Query(filter, columns).Rows.Select(r => r.ToObject(type)).ToArray();
  622. if (updates.Any())
  623. {
  624. foreach (var update in updates)
  625. CoreUtils.SetPropertyValue(update, propname, targetid);
  626. ClientFactory.CreateClient(type).Save(updates,
  627. string.Format("Merged {0} Records", typeof(TEntity).EntityName().Split('.').Last()));
  628. }
  629. }
  630. }
  631. var histories = new Client<AuditTrail>()
  632. .Query(new Filter<AuditTrail>(x => x.EntityID).InList(otherids), new Columns<AuditTrail>(x => x.EntityID)).Rows
  633. .Select(x => x.ToObject<AuditTrail>()).ToArray();
  634. foreach (var history in histories)
  635. history.EntityID = targetid;
  636. if (histories.Any())
  637. new Client<AuditTrail>().Save(histories, "");
  638. var deletes = new List<object>();
  639. foreach (var otherid in otherids)
  640. {
  641. var delete = new TEntity();
  642. CoreUtils.SetPropertyValue(delete, "ID", otherid);
  643. deletes.Add(delete);
  644. }
  645. ClientFactory.CreateClient(typeof(TEntity))
  646. .Delete(deletes, string.Format("Merged {0} Records", typeof(TEntity).EntityName().Split('.').Last()));
  647. return true;
  648. }
  649. }
  650. protected override IEnumerable<TEntity> LoadDuplicatorItems(CoreRow[] rows)
  651. {
  652. return rows.Select(x => x.ToObject<TEntity>());
  653. }
  654. protected override bool BeforePaste(IEnumerable<TEntity> items, ClipAction action)
  655. {
  656. if (action == ClipAction.Copy)
  657. {
  658. foreach (var item in items)
  659. item.ID = Guid.Empty;
  660. return true;
  661. }
  662. return base.BeforePaste(items, action);
  663. }
  664. protected override void CustomiseExportFilters(Filters<TEntity> filters, CoreRow[] visiblerows)
  665. {
  666. base.CustomiseExportFilters(filters, visiblerows);
  667. var ids = visiblerows.Select(r => r.Get<TEntity, Guid>(c => c.ID)).ToArray();
  668. filters.Add(new Filter<TEntity>(x => x.ID).InList(ids));
  669. }
  670. protected override IEnumerable<Tuple<Type?, CoreTable>> LoadExportTables(Filters<TEntity> filter, IEnumerable<Tuple<Type, IColumns>> tableColumns)
  671. {
  672. var queries = new Dictionary<string, IQueryDef>();
  673. var columns = tableColumns.ToList();
  674. foreach (var table in columns)
  675. {
  676. var tableType = table.Item1;
  677. PropertyInfo? property = null;
  678. var m2m = CoreUtils.GetManyToMany(tableType, typeof(TEntity));
  679. IFilter? queryFilter = null;
  680. if (m2m != null)
  681. {
  682. property = CoreUtils.GetManyToManyThisProperty(tableType, typeof(TEntity));
  683. }
  684. else
  685. {
  686. var o2m = CoreUtils.GetOneToMany(tableType, typeof(TEntity));
  687. if (o2m != null)
  688. {
  689. property = CoreUtils.GetOneToManyProperty(tableType, typeof(TEntity));
  690. }
  691. }
  692. if (property != null)
  693. {
  694. var subQuery = new SubQuery<TEntity>();
  695. subQuery.Filter = filter.Combine();
  696. subQuery.Column = new Column<TEntity>(x => x.ID);
  697. queryFilter = (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(tableType)) as IFilter)!;
  698. queryFilter.Expression = CoreUtils.GetMemberExpression(tableType, property.Name + ".ID");
  699. queryFilter.InQuery(subQuery);
  700. queries[tableType.Name] = new QueryDef(tableType)
  701. {
  702. Filter = queryFilter,
  703. Columns = table.Item2
  704. };
  705. }
  706. }
  707. var results = Client.QueryMultiple(queries);
  708. return columns.Select(x => new Tuple<Type?, CoreTable>(x.Item1, results[x.Item1.Name]));
  709. }
  710. }
  711. }