DynamicGridUtils.cs 28 KB

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