DynamicGridUtils.cs 26 KB

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