DynamicGridUtils.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  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.Core;
  12. using InABox.Wpf;
  13. using InABox.Core.Reports;
  14. using Syncfusion.Data.Extensions;
  15. using System.Diagnostics.CodeAnalysis;
  16. using System.Data;
  17. using System.Windows.Media;
  18. namespace InABox.DynamicGrid;
  19. [Caption("Set Default Column Selections")]
  20. public class CanSetDefaultColumns : EnabledSecurityDescriptor<CoreLicense>
  21. {
  22. }
  23. [LibraryInitializer]
  24. public static class DynamicGridUtils
  25. {
  26. private static IEnumerable<Type>? _allm2mtypes;
  27. private static IEnumerable<Type>? _allm2mpages;
  28. private static IEnumerable<Type>? _allo2mtypes;
  29. private static IEnumerable<Type>? _allo2mpages;
  30. private static IEnumerable<Type>? _allcepages;
  31. private static IEnumerable<Type>? _alleltypes;
  32. private static Dictionary<Type, IList<Type>> _onetomanypages = new();
  33. private static Dictionary<Type, IList<Type>> _manytomanytomanypages = new();
  34. private static Dictionary<Type, IList<Tuple<Type, PropertyInfo>>> _enclosedlistpages = new();
  35. private static Dictionary<Type, IList<Type>> _customeditorpages = new();
  36. // HACK: These are really dumb
  37. public static Action<ReportTemplate, DataModel>? PreviewReport { get; set; }
  38. public static Action<FrameworkElement?, string, DataModel, bool>? PrintMenu { get; set; }
  39. public static readonly MainResources Resources = new();
  40. public static void RegisterClasses()
  41. {
  42. // String assyname = "_" + Assembly.GetExecutingAssembly().GetName().Name;
  43. // AssemblyName assemblyName = new AssemblyName(assyname);
  44. // AppDomain appDomain = Thread.GetDomain();
  45. //
  46. // String assyFile = String.Format("{0}.dll", assemblyName.Name);
  47. // String path = "";
  48. // if (Assembly.GetEntryAssembly() != null)
  49. // {
  50. // path = Path.Combine(
  51. // Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
  52. // Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location)
  53. // );
  54. // }
  55. // else
  56. // {
  57. // path = Path.Combine(
  58. // Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
  59. // Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location)
  60. // );
  61. // }
  62. //
  63. // if (!Directory.Exists(path))
  64. // Directory.CreateDirectory(path);
  65. // AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, path);
  66. //
  67. // ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assyFile); //,true);
  68. //
  69. // if (_allm2mtypes == null)
  70. // {
  71. // _allm2mtypes = CoreUtils.TypeList(
  72. // AppDomain.CurrentDomain.GetAssemblies(),
  73. // x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>))
  74. // );
  75. // }
  76. //
  77. // var maps = _allm2mtypes.Where(x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Last().Equals(typeof(Document))));
  78. //
  79. // foreach (var map in maps)
  80. // {
  81. // var intf = map.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Last().Equals(typeof(Document)));
  82. // Type entity = intf.GenericTypeArguments.First();
  83. // Type basetype = typeof(DynamicDocumentGrid<,>).MakeGenericType(map, entity);
  84. // TypeBuilder tbService = module.DefineType(String.Format("{0}", map.EntityName().Replace(".", "_")), TypeAttributes.Public | TypeAttributes.Class);
  85. // tbService.SetParent(basetype);
  86. // Type final = tbService.CreateType();
  87. // }
  88. //
  89. // try
  90. // {
  91. // assemblyBuilder.Save(assyFile);
  92. // }
  93. // catch (Exception e)
  94. // {
  95. // Logger.Send(LogType.Error, "", String.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  96. // }
  97. }
  98. #region Pages
  99. public static IEnumerable<Type> GetManyToManyTypes(Type type)
  100. {
  101. _allm2mtypes ??= CoreUtils.TypeList(
  102. AppDomain.CurrentDomain.GetAssemblies(),
  103. x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>))
  104. );
  105. return _allm2mtypes.Where(x => x.GetInterfaces().Any(
  106. i => i.IsGenericType
  107. && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>)
  108. && i.GenericTypeArguments[0] == type)
  109. );
  110. }
  111. public static void LoadManyToManyPages(Type type, DynamicEditorPages pages)
  112. {
  113. if (!_manytomanytomanypages.TryGetValue(type, out var pageTypes))
  114. {
  115. pageTypes = new List<Type>();
  116. var maps = GetManyToManyTypes(type);
  117. foreach (var map in maps)
  118. {
  119. if (ClientFactory.IsSupported(map))
  120. {
  121. _allm2mpages ??= CoreUtils.TypeList(
  122. AppDomain.CurrentDomain.GetAssemblies(),
  123. x => x.IsClass
  124. && !x.IsAbstract
  125. && !x.IsGenericType
  126. && x.GetInterfaces().Any(
  127. i => i.IsGenericType
  128. && i.GetGenericTypeDefinition() == typeof(IDynamicManyToManyGrid<,>)
  129. )
  130. );
  131. var subtypes = _allm2mpages.Where(
  132. x => x.GetInterfaces().Any(i =>
  133. i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicManyToManyGrid<,>) &&
  134. i.GenericTypeArguments.First().Equals(map) && i.GenericTypeArguments.Last().Equals(type))
  135. );
  136. if (subtypes.Any())
  137. {
  138. pageTypes.Add(subtypes.First());
  139. }
  140. else
  141. {
  142. pageTypes.Add(typeof(DynamicManyToManyGrid<,>).MakeGenericType(map, type));
  143. }
  144. }
  145. }
  146. _manytomanytomanypages[type] = pageTypes.ToArray();
  147. }
  148. pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x) as IDynamicEditorPage)!));
  149. }
  150. public static IEnumerable<Type> GetOneToManyTypes(Type type)
  151. {
  152. _allo2mtypes ??= CoreUtils.TypeList(
  153. AppDomain.CurrentDomain.GetAssemblies(),
  154. x => x.GetInterfaces().Any(i =>
  155. i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IOneToMany<>) && x.GetCustomAttribute<ObsoleteAttribute>() == null)
  156. );
  157. return _allo2mtypes
  158. .Where(x => x.GetInterfaces().Any(i =>
  159. i.IsGenericType
  160. && i.GetGenericTypeDefinition() == typeof(IOneToMany<>)
  161. && i.GenericTypeArguments.Contains(type)))
  162. .OrderBy(x => x.EntityName());
  163. }
  164. public static void LoadOneToManyPages(Type type, DynamicEditorPages pages)
  165. {
  166. if (!_onetomanypages.TryGetValue(type, out var pageTypes))
  167. {
  168. pageTypes = new List<Type>();
  169. var maps = GetOneToManyTypes(type);
  170. foreach (var map in maps)
  171. {
  172. if (ClientFactory.IsSupported(map))
  173. {
  174. _allo2mpages ??= CoreUtils.TypeList(
  175. AppDomain.CurrentDomain.GetAssemblies(),
  176. x =>
  177. x.IsClass
  178. && !x.IsAbstract
  179. && !x.IsGenericType
  180. && x.GetInterfaces().Any(i =>
  181. i.IsGenericType
  182. && i.GetGenericTypeDefinition() == typeof(IDynamicOneToManyGrid<,>)
  183. )
  184. );
  185. var subtypes = _allo2mpages.Where(x => x.GetInterfaces().Any(
  186. i => i.IsGenericType
  187. && i.GetGenericTypeDefinition() == typeof(IDynamicOneToManyGrid<,>)
  188. && i.GenericTypeArguments.First().Equals(type)
  189. && i.GenericTypeArguments.Last().Equals(map)
  190. )
  191. );
  192. if (subtypes.Any())
  193. {
  194. pageTypes.Add(subtypes.First());
  195. }
  196. else
  197. {
  198. pageTypes.Add(typeof(DynamicOneToManyGrid<,>).MakeGenericType(type, map));
  199. }
  200. }
  201. }
  202. _onetomanypages[type] = pageTypes.ToArray();
  203. }
  204. pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x) as IDynamicEditorPage)!));
  205. }
  206. public static void LoadCustomEditorPages(Type type, DynamicEditorPages pages)
  207. {
  208. if (!_customeditorpages.TryGetValue(type, out var pageTypes))
  209. {
  210. _allcepages ??= CoreUtils.TypeList(
  211. AppDomain.CurrentDomain.GetAssemblies(),
  212. x => x.IsClass
  213. && !x.IsAbstract
  214. && !x.IsGenericType
  215. && x.GetInterfaces().Any(i =>
  216. i.IsGenericType
  217. && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>)
  218. )
  219. );
  220. pageTypes = _allcepages.Where(x => x.GetInterfaces().Any(i =>
  221. i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>) &&
  222. i.GenericTypeArguments.First().Equals(type))).ToArray();
  223. _customeditorpages[type] = pageTypes;
  224. }
  225. pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x) as IDynamicEditorPage)!));
  226. }
  227. public static void LoadEnclosedListPages(Type type, DynamicEditorPages pages)
  228. {
  229. if (!_enclosedlistpages.TryGetValue(type, out var pageTypes))
  230. {
  231. pageTypes = new List<Tuple<Type, PropertyInfo>>();
  232. foreach (var property in type.GetProperties())
  233. {
  234. if (property.PropertyType.GetInterfaces().Contains(typeof(IList)))
  235. {
  236. var curtype = property.PropertyType;
  237. var gentype = property.PropertyType.GetGenericArguments().FirstOrDefault();
  238. while (gentype == null && curtype?.BaseType != null)
  239. {
  240. curtype = curtype.BaseType;
  241. gentype = curtype?.GetGenericArguments().FirstOrDefault();
  242. }
  243. if (gentype != null)
  244. if (gentype.IsSubclassOf(typeof(BaseObject)))
  245. {
  246. var editor = property.GetCustomAttributes().FirstOrDefault(x => x is BaseEditor);
  247. if (editor == null || !(editor is NullEditor))
  248. {
  249. _alleltypes ??= CoreUtils.TypeList(
  250. AppDomain.CurrentDomain.GetAssemblies(),
  251. x => x.IsClass
  252. && !x.IsAbstract
  253. && !x.IsGenericType
  254. && x.GetInterfaces().Any(i =>
  255. i.IsGenericType
  256. && i.GetGenericTypeDefinition() == typeof(IDynamicEnclosedListGrid<,>)
  257. )
  258. );
  259. var subtypes = _alleltypes.Where(
  260. x => x.GetInterfaces().Any(
  261. i => i.IsGenericType
  262. && i.GetGenericTypeDefinition() == typeof(IDynamicEnclosedListGrid<,>)
  263. && i.GenericTypeArguments.First().Equals(type)
  264. && i.GenericTypeArguments.Last().Equals(gentype)
  265. )
  266. );
  267. if (subtypes.Any())
  268. {
  269. pageTypes.Add(new(subtypes.First(), property));
  270. }
  271. else
  272. {
  273. subtypes = _alleltypes.Where(x => x.GetInterfaces().Any(i => (i.GenericTypeArguments.LastOrDefault()?.Equals(gentype) == true)));
  274. if (subtypes.Any())
  275. {
  276. pageTypes.Add(new(subtypes.First().MakeGenericType(type), property));
  277. }
  278. else
  279. {
  280. try
  281. {
  282. pageTypes.Add(new(typeof(DynamicEnclosedListGrid<,>).MakeGenericType(type, gentype), property));
  283. }
  284. catch (Exception e)
  285. {
  286. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  287. }
  288. }
  289. }
  290. }
  291. }
  292. }
  293. }
  294. _enclosedlistpages[type] = pageTypes.ToArray();
  295. }
  296. pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x.Item1, x.Item2) as IDynamicEditorPage)!));
  297. }
  298. #endregion
  299. #region Columns
  300. public static Columns<T> LoadEditorColumns<T>(Columns<T> additional)
  301. {
  302. var result = new Columns<T>().Default(
  303. ColumnType.IncludeOptional,
  304. ColumnType.IncludeForeignKeys,
  305. ColumnType.IncludeUserProperties,
  306. ColumnType.IncludeEditable);
  307. foreach (var col in additional.Items)
  308. if (!result.Items.Any(x => string.Equals(x.Property, col.Property)))
  309. result.Add(col.Property);
  310. foreach (var col in result.Items)
  311. {
  312. var prop = DatabaseSchema.Property(typeof(T), col.Property);
  313. if (prop?.Editor is DataLookupEditor dataLookup)
  314. {
  315. foreach (var lookupColumn in LookupFactory.DefineLookupFilterColumns(typeof(T), prop.Name).ColumnNames())
  316. {
  317. result.Add(lookupColumn);
  318. }
  319. }
  320. }
  321. return result;
  322. }
  323. #endregion
  324. #region Editor Values
  325. public static Dictionary<string, object?> UpdateEditorValue(BaseObject[] items, string name, object? value)
  326. {
  327. Logger.Send(LogType.Information, "", string.Format("DynamicGridUtils.UpdateEditorValue({0},{1},{2})", items.Length, name, value));
  328. var sw = new Stopwatch();
  329. var changes = new Dictionary<string, object?>();
  330. var props = DatabaseSchema.Properties(items.First().GetType()).ToArray();
  331. foreach (var item in items)
  332. {
  333. //Dictionary<String, object> previous = new Dictionary<string, object>();
  334. var previous = CoreUtils.GetValues(item, props);
  335. //if (item.OriginalValues != null)
  336. //{
  337. // foreach (var key in item.OriginalValues.Keys)
  338. // previous[key] = item.OriginalValues[key];
  339. //}
  340. var prop = DatabaseSchema.Property(item.GetType(), name);
  341. if (prop is CustomProperty)
  342. {
  343. if (!item.HasOriginalValue(name))
  344. item.SetOriginalValue(name, item.UserProperties[name]);
  345. item.UserProperties[name] = value;
  346. }
  347. else
  348. {
  349. if (prop != null)
  350. try
  351. {
  352. var getter = prop.Getter();
  353. var oldvalue = getter != null ? getter.Invoke(item) : CoreUtils.GetPropertyValue(item, name);
  354. item.OnPropertyChanged(name, oldvalue, value);
  355. var setter = prop.Setter();
  356. if (setter != null && value != null)
  357. setter.Invoke(item, value);
  358. else
  359. CoreUtils.SetPropertyValue(item, name, value);
  360. }
  361. catch (Exception)
  362. {
  363. Logger.Send(LogType.Error, "",
  364. string.Format("Unable to Set Value for [{0}.{1}] (Value is {2})", item.GetType().Name, name, value));
  365. }
  366. }
  367. var current = CoreUtils.GetValues(item, props);
  368. CoreUtils.MergeChanges(previous, current, changes);
  369. }
  370. return changes;
  371. }
  372. public static void UpdateEditorValue(BaseObject[] items, string name, object? value, Dictionary<string, object?> changes)
  373. {
  374. var results = UpdateEditorValue(items, name, value);
  375. foreach (var key in results.Keys)
  376. changes[key] = results[key];
  377. }
  378. #endregion
  379. #region Dynamic Grid Creation
  380. public static IDynamicGrid CreateDynamicGrid(Type gridType, Type entityType)
  381. {
  382. var type = FindDynamicGrid(gridType, entityType);
  383. return (Activator.CreateInstance(type) as IDynamicGrid)
  384. ?? throw new ArgumentException("Argument must be a type of IDynamicGrid", nameof(gridType));
  385. }
  386. public static DynamicGrid<TEntity> CreateDynamicGrid<TEntity>(Type gridType)
  387. where TEntity : BaseObject, new()
  388. {
  389. var type = FindDynamicGrid(gridType, typeof(TEntity));
  390. return (Activator.CreateInstance(type) as DynamicGrid<TEntity>)
  391. ?? throw new ArgumentException("Argument must be a type of IDynamicGrid", nameof(gridType));
  392. }
  393. private static Dictionary<Type, Type[]> _dynamicGrids = new();
  394. public static bool TryFindDynamicGrid(Type gridType, Type entityType, [NotNullWhen(true)] out Type? grid)
  395. {
  396. if (!_dynamicGrids.TryGetValue(gridType, out var grids))
  397. {
  398. grids = CoreUtils.TypeList(
  399. AppDomain.CurrentDomain.GetAssemblies(),
  400. myType =>
  401. myType.IsClass
  402. && !myType.IsAbstract
  403. && !myType.IsGenericType
  404. && myType.IsAssignableTo(typeof(IDynamicGrid))
  405. && !myType.IsAssignableTo(typeof(ISpecificGrid))
  406. ).ToArray();
  407. _dynamicGrids[gridType] = grids;
  408. }
  409. grids = grids.Where(x => x.IsSubclassOfRawGeneric(gridType)).ToArray();
  410. var entityGrids = grids.Where(x =>
  411. {
  412. var baseGrid = x.GetSuperclassDefinition(typeof(DynamicGrid<>));
  413. return baseGrid?.GenericTypeArguments[0] == entityType;
  414. }).ToList();
  415. var defaults = entityGrids.Where(x => x.IsAssignableTo(typeof(IDefaultGrid))).ToList();
  416. if (defaults.Count > 0)
  417. {
  418. if (defaults.Count > 1)
  419. {
  420. Logger.Send(LogType.Information, ClientFactory.UserID, $"Error: {defaults.Count} IDefaultGrid derivations for {gridType.Name} of {entityType.Name}");
  421. }
  422. grid = defaults.First();
  423. return true;
  424. }
  425. grid = entityGrids.FirstOrDefault();
  426. return grid is not null;
  427. }
  428. public static Type FindDynamicGrid(Type gridType, Type entityType)
  429. {
  430. if(TryFindDynamicGrid(gridType, entityType, out var grid))
  431. {
  432. return grid;
  433. }
  434. return gridType.MakeGenericType(entityType);
  435. }
  436. public static Window CreateGridWindow(string title, IDynamicGrid dynamicGrid)
  437. {
  438. dynamicGrid.Margin = new Thickness(5);
  439. var window = new ThemableWindow { Title = title, Content = dynamicGrid };
  440. dynamicGrid.Refresh(true, true);
  441. return window;
  442. }
  443. public static Window CreateGridWindow(string title, Type entityType, Type? gridType = null)
  444. {
  445. gridType ??= typeof(DynamicGrid<>);
  446. var grid = CreateDynamicGrid(gridType, entityType);
  447. return CreateGridWindow(title, grid);
  448. }
  449. public static Window CreateGridWindow<TGrid, TEntity>(string title)
  450. where TEntity : BaseObject
  451. where TGrid : IDynamicGrid
  452. {
  453. return CreateGridWindow(title, typeof(TEntity), typeof(TGrid));
  454. }
  455. public static Window CreateGridWindow<TEntity>(string title)
  456. where TEntity : BaseObject
  457. {
  458. return CreateGridWindow(title, typeof(TEntity));
  459. }
  460. #endregion
  461. #region Editing BaseObject
  462. /// <summary>
  463. /// Edit (using <see cref="DynamicItemsListGrid{T}"/>) a list of <see cref="BaseObject"/>s. Use for objects not saved in the database.
  464. /// </summary>
  465. /// <typeparam name="T"></typeparam>
  466. /// <param name="items"></param>
  467. /// <param name="pageDataHandler"></param>
  468. /// <param name="preloadPages"></param>
  469. /// <returns></returns>
  470. public static bool EditObjects<T>(T[] items, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)
  471. where T : BaseObject, new()
  472. {
  473. var grid = new DynamicItemsListGrid<T>();
  474. customiseGrid?.Invoke(grid);
  475. return grid.EditItems(items, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);
  476. }
  477. /// <summary>
  478. /// Edit (using <see cref="DynamicItemsListGrid{T}"/>) a <see cref="BaseObject"/>s. Use for objects not saved in the database.
  479. /// </summary>
  480. /// <typeparam name="T"></typeparam>
  481. /// <param name="items"></param>
  482. /// <param name="pageDataHandler"></param>
  483. /// <param name="preloadPages"></param>
  484. /// <returns></returns>
  485. public static bool EditObject<T>(T item, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)
  486. where T : BaseObject, new()
  487. {
  488. var grid = new DynamicItemsListGrid<T>();
  489. customiseGrid?.Invoke(grid);
  490. return grid.EditItems(new T[] { item }, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);
  491. }
  492. /// <summary>
  493. /// Edit (using a grid sourced with <see cref="CreateDynamicGrid{T}(Type)"/>) a <typeparamref name="T"/>.
  494. /// </summary>
  495. /// <typeparam name="T"></typeparam>
  496. /// <param name="item"></param>
  497. /// <param name="pageDataHandler"></param>
  498. /// <param name="preloadPages"></param>
  499. /// <param name="customiseGrid"></param>
  500. /// <returns></returns>
  501. public static bool EditEntity<T>(T item, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)
  502. where T : Entity, new()
  503. {
  504. var grid = CreateDynamicGrid<T>(typeof(DynamicGrid<>));
  505. customiseGrid?.Invoke(grid);
  506. return grid.EditItems(new T[] { item }, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);
  507. }
  508. /// <summary>
  509. /// Edit (using a grid sourced with <see cref="CreateDynamicGrid{T}(Type)"/>) a list of <typeparamref name="T"/>.
  510. /// </summary>
  511. /// <typeparam name="T"></typeparam>
  512. /// <param name="item"></param>
  513. /// <param name="pageDataHandler"></param>
  514. /// <param name="preloadPages"></param>
  515. /// <param name="customiseGrid"></param>
  516. /// <returns></returns>
  517. public static bool EditEntities<T>(T[] items, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)
  518. where T : Entity, new()
  519. {
  520. var grid = CreateDynamicGrid<T>(typeof(DynamicGrid<>));
  521. customiseGrid?.Invoke(grid);
  522. return grid.EditItems(items, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);
  523. }
  524. #endregion
  525. #region Drag + Drop
  526. public static string DragFormat => typeof(DynamicGridDragFormat).FullName ?? "";
  527. /// <summary>
  528. /// Try to get data dragged from a <see cref="DynamicGrid{T}"/> from a <see cref="DragEventArgs"/>, returning <see langword="true"/>
  529. /// if data was present.
  530. /// </summary>
  531. /// <param name="e"></param>
  532. public static bool TryGetDropData(
  533. DragEventArgs e,
  534. [NotNullWhen(true)] out Type? type,
  535. [NotNullWhen(true)] out CoreTable? table)
  536. {
  537. if (e.Data.GetDataPresent(DragFormat))
  538. {
  539. var data = e.Data.GetData(DragFormat) as DynamicGridDragFormat;
  540. if (data is not null)
  541. {
  542. table = new CoreTable();
  543. foreach (var column in data.Table.Columns)
  544. {
  545. if (column is DataColumn dataColumn)
  546. {
  547. table.Columns.Add(new CoreColumn { ColumnName = dataColumn.ColumnName.Replace('_', '.'), DataType = dataColumn.DataType });
  548. }
  549. }
  550. foreach (var row in data.Table.Rows)
  551. {
  552. if (row is DataRow dataRow)
  553. {
  554. var coreRow = table.NewRow();
  555. coreRow.LoadValues(dataRow.ItemArray);
  556. table.Rows.Add(coreRow);
  557. }
  558. }
  559. type = data.Entity;
  560. return true;
  561. }
  562. }
  563. table = null;
  564. type = null;
  565. return false;
  566. }
  567. #endregion
  568. #region Style
  569. public static Brush SelectionBackground { get; set; } = new SolidColorBrush(Colors.Black);
  570. public static Brush SelectionForeground { get; set; } = new SolidColorBrush(Colors.Silver);
  571. public static Brush FilterBackground { get; set; } = new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0xE9, 0xF7, 0xC9));
  572. #endregion
  573. public static void PopulateFormMenu<TEntityForm, TEntity, TEntityLink>(
  574. ItemsControl menu,
  575. Guid entityID,
  576. Func<TEntity> loadEntity,
  577. bool editOnAdd = false,
  578. DynamicFormEditWindow.CustomiseDynamicFormEditWindow? customiseEditor = null)
  579. where TEntityForm : EntityForm<TEntity, TEntityLink, TEntityForm>, new()
  580. where TEntity : Entity
  581. where TEntityLink : IEntityLink<TEntity>, new()
  582. {
  583. var task = Task.Run(() =>
  584. {
  585. return new Client<TEntityForm>().Query(
  586. new Filter<TEntityForm>(x => x.Parent.ID).IsEqualTo(entityID),
  587. null).Rows.Select(x => x.ToObject<TEntityForm>()).ToList();
  588. });
  589. var addForm = new MenuItem { Header = "Add Form" };
  590. addForm.Click += (o, e) =>
  591. {
  592. var entity = loadEntity();
  593. var filter = LookupFactory.DefineChildFilter<TEntity, DigitalForm>(new TEntity[] { entity })
  594. ?? LookupFactory.DefineLookupFilter<TEntityForm, DigitalForm, DigitalFormLink>(x => x.Form, Array.Empty<TEntityForm>());
  595. var select = new MultiSelectDialog<DigitalForm>(
  596. filter,
  597. LookupFactory.DefineLookupColumns<TEntityForm, DigitalForm, DigitalFormLink>(x => x.Form).Add(x => x.Description),
  598. false);
  599. if(select.ShowDialog() == true)
  600. {
  601. var digitalForm = select.Data().Rows.FirstOrDefault()?.ToObject<DigitalForm>();
  602. if(digitalForm is not null)
  603. {
  604. var form = new TEntityForm
  605. {
  606. Description = digitalForm.Description
  607. };
  608. form.Parent.ID = entityID;
  609. form.Form.ID = digitalForm.ID;
  610. if (editOnAdd)
  611. {
  612. if (DynamicFormEditWindow.EditDigitalForm(form, out var dataModel, customise: customiseEditor))
  613. {
  614. dataModel.Update(null);
  615. }
  616. }
  617. else
  618. {
  619. new Client<TEntityForm>().Save(form, "Added by user");
  620. }
  621. }
  622. };
  623. };
  624. var manageForms = new MenuItem { Header = "Manage Forms..." };
  625. manageForms.Click += (o, e) =>
  626. {
  627. var window = new ThemableWindow() { Title = $"Manage {typeof(TEntity).Name} Forms" };
  628. var grid = new DynamicEntityFormGrid<TEntityForm, TEntity, TEntityLink>(loadEntity());
  629. grid.Refresh(true, true);
  630. grid.Margin = new Thickness(5);
  631. window.Content = grid;
  632. window.ShowDialog();
  633. };
  634. menu.Items.Add(addForm);
  635. menu.Items.Add(new Separator());
  636. menu.Items.Add(new MenuItem() { Header = "Loading...", IsEnabled = false });
  637. menu.Items.Add(new Separator());
  638. menu.Items.Add(manageForms);
  639. task.ContinueWith((task) =>
  640. {
  641. var entityForms = task.Result;
  642. menu.Items.Clear();
  643. menu.Items.Add(addForm);
  644. menu.Items.Add(new Separator());
  645. if (entityForms.Any())
  646. {
  647. foreach (var entityForm in entityForms)
  648. {
  649. var description = entityForm.Description;
  650. if (string.IsNullOrWhiteSpace(description))
  651. {
  652. description = entityForm.Form.Description;
  653. }
  654. var formItem = new MenuItem { Header = $"{entityForm.Number} : {description}" };
  655. formItem.Click += (o, e) =>
  656. {
  657. if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel, customise: customiseEditor))
  658. {
  659. dataModel.Update(null);
  660. }
  661. };
  662. menu.Items.Add(formItem);
  663. }
  664. }
  665. else
  666. {
  667. menu.Items.Add(new MenuItem() { Header = "No Forms", IsEnabled = false });
  668. }
  669. menu.Items.Add(new Separator());
  670. menu.Items.Add(manageForms);
  671. }, TaskScheduler.FromCurrentSynchronizationContext());
  672. }
  673. }