DynamicEditorGrid.xaml.cs 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Markup;
  13. using System.Windows.Media;
  14. using System.Windows.Threading;
  15. using System.Xml;
  16. using InABox.Clients;
  17. using InABox.Configuration;
  18. using InABox.Core;
  19. using InABox.WPF;
  20. using NPOI.SS.Formula.Functions;
  21. namespace InABox.DynamicGrid
  22. {
  23. /// <summary>
  24. /// Interaction logic for DynamicEditorGrid.xaml
  25. /// </summary>
  26. public delegate void OnUpdateOtherEditorHandler(string columnname, object value);
  27. public delegate Dictionary<string, object?> EditorValueChangedHandler(object sender, string name, object value);
  28. //public delegate Dictionary<object, object> EditorGetLookupsHandler(object sender, String column);
  29. public partial class DynamicEditorGrid : UserControl
  30. {
  31. public delegate void EditorCreatedHandler(object sender, double height, double width);
  32. public delegate Document? FindDocumentEvent(string FileName);
  33. public delegate Document? GetDocumentEvent(Guid id);
  34. public delegate object? GetPropertyValueHandler(object sender, string name);
  35. public delegate void SaveDocumentEvent(Document document);
  36. public delegate void SetPropertyValueHandler(object sender, string name, object value);
  37. public delegate object?[] GetItemsEvent();
  38. // Column Definitions as defined by calling model
  39. private DynamicGridColumns _columns;
  40. private string _layoutname = "";
  41. private bool _loaded;
  42. private bool bChanging;
  43. // List of Changed Editor Values (used when redesigning screen)
  44. private readonly Dictionary<string, object> changes = new();
  45. private Grid CustomGrid;
  46. private DynamicTabControl Details;
  47. private Grid EditorGrid;
  48. // Active Editor List
  49. private readonly List<IDynamicEditorControl> editors = new();
  50. private readonly Dictionary<IDynamicEditorPage, DynamicTabItem> pagemap = new();
  51. //Dictionary<IDynamicEditorPage, bool> loadedpages = new Dictionary<IDynamicEditorPage, bool>();
  52. public DynamicEditorGrid()
  53. {
  54. InitializeComponent();
  55. Loaded += DynamicEditorGrid_Loaded;
  56. }
  57. //private Dictionary<String, Tuple<FrameworkElement, Object, DynamicGridColumn>> editors = new Dictionary<string, Tuple<FrameworkElement, Object, DynamicGridColumn>>();
  58. public DynamicEditorPages? Pages { get; private set; }
  59. public bool PreloadPages { get; set; }
  60. public Type UnderlyingType { get; set; }
  61. public OnLoadPage? OnLoadPage { get; set; }
  62. public OnSelectPage? OnSelectPage { get; set; }
  63. public OnUnloadPage? OnUnloadPage { get; set; }
  64. //private void UnloadEditorValues()
  65. //{
  66. // foreach (string columnname in editors.Keys)
  67. // {
  68. // bool changed = (bool)CoreUtils.GetPropertyValue(editors[columnname], "Changed");
  69. // if (changed)
  70. // {
  71. // changes[columnname] = CoreUtils.GetPropertyValue(editors[columnname], "Value");
  72. // Dictionary<string, object> othervalues = CoreUtils.GetPropertyValue(editors[columnname], "OtherValues") as Dictionary<string, object>;
  73. // foreach (var field in othervalues.Keys)
  74. // {
  75. // List<String> comps = columnname.Split('.').ToList();
  76. // comps[comps.Count - 1] = field;
  77. // String actualname = String.Join(".", comps);
  78. // changes[actualname] = othervalues[field];
  79. // }
  80. // }
  81. // }
  82. //}
  83. public bool IsCustomLayout { get; private set; }
  84. public bool TryFindEditor(string columnname, [NotNullWhen(true)] out IDynamicEditorControl? editor)
  85. {
  86. editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
  87. return editor is not null;
  88. }
  89. public IDynamicEditorControl? FindEditor(string columnname)
  90. {
  91. TryFindEditor(columnname, out var editor);
  92. return editor;
  93. }
  94. public object? GetPropertyValue(string columnname)
  95. {
  96. return OnGetPropertyValue?.Invoke(this, columnname);
  97. }
  98. public event EditorCreatedHandler? OnEditorCreated;
  99. public event OnCustomiseColumns? OnCustomiseColumns;
  100. public event OnGetEditor? OnGetEditor;
  101. public event OnGridCustomiseEditor? OnGridCustomiseEditor;
  102. public event OnGetEditorSequence? OnGetSequence;
  103. public event GetPropertyValueHandler? OnGetPropertyValue;
  104. public event SetPropertyValueHandler? OnSetPropertyValue;
  105. public event EditorValueChangedHandler? OnEditorValueChanged;
  106. public event OnReconfigureEditors? ReconfigureEditors;
  107. public event OnDefineFilter? OnDefineFilter;
  108. public event OnDefineLookup? OnDefineLookups;
  109. public event OnLookupsDefined? OnLookupsDefined;
  110. public event GetDocumentEvent? OnGetDocument;
  111. public event FindDocumentEvent? OnFindDocument;
  112. public event SaveDocumentEvent? OnSaveDocument;
  113. public event GetItemsEvent? GetItems;
  114. private void DynamicEditorGrid_Loaded(object sender, RoutedEventArgs e)
  115. {
  116. Reload();
  117. }
  118. public void Reload(bool refresh = false)
  119. {
  120. if (_loaded ^ refresh) return;
  121. ConfigureEditors();
  122. LoadEditorValues();
  123. ClearPages();
  124. AddPages();
  125. DoReconfigureEditors();
  126. //MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
  127. //MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
  128. //if (Keyboard.PrimaryDevice != null)
  129. //{
  130. // if (Keyboard.PrimaryDevice.ActiveSource != null)
  131. // {
  132. // var e1 = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Tab) { RoutedEvent = Keyboard.KeyDownEvent };
  133. // InputManager.Current.ProcessInput(e1);
  134. // }
  135. //}
  136. editors.FirstOrDefault()?.SetFocus();
  137. _loaded = true;
  138. }
  139. #region Configure Editors
  140. private void LoadLookupColumns(DynamicGridColumn column, Dictionary<string, string> othercolumns)
  141. {
  142. othercolumns.Clear();
  143. var comps = column.ColumnName.Split('.').ToList();
  144. comps.RemoveAt(comps.Count - 1);
  145. var prefix = string.Format("{0}.", string.Join(".", comps));
  146. var cols = _columns.Where(x => !x.ColumnName.Equals(column.ColumnName) && x.ColumnName.StartsWith(prefix));
  147. foreach (var col in cols)
  148. othercolumns[col.ColumnName.Replace(prefix, "")] = col.ColumnName;
  149. }
  150. private string GetCodeColumn(DynamicGridColumn column)
  151. {
  152. var comps = column.ColumnName.Split('.').ToList();
  153. comps.RemoveAt(comps.Count - 1);
  154. var prefix = string.Format("{0}.", string.Join(".", comps));
  155. var cols = _columns.Where(x => !x.ColumnName.Equals(column.ColumnName) && x.ColumnName.StartsWith(prefix));
  156. foreach (var col in cols)
  157. {
  158. var editor = OnGetEditor?.Invoke(col);
  159. if (editor is CodeEditor || editor is UniqueCodeEditor)
  160. return col.ColumnName.Split('.').Last();
  161. }
  162. return "";
  163. }
  164. private void ConfigureExpressionEditor(ExpressionEditorControl control)
  165. {
  166. control.GetItems += () => GetItems?.Invoke() ?? Array.Empty<object?>();
  167. }
  168. private void ConfigurePopupEditor(PopupEditorControl popup, DynamicGridColumn column, PopupEditor editor)
  169. {
  170. popup.ColumnName = column.ColumnName;
  171. LoadLookupColumns(column, popup.OtherColumns);
  172. if (popup.EditorDefinition is DataLookupEditor dataLookup)
  173. LoadLookupColumns(column, dataLookup.OtherColumns);
  174. popup.OnDefineFilter += (sender, type) => { return OnDefineFilter?.Invoke(sender, type); };
  175. popup.OnUpdateOtherEditor += Lookup_OnUpdateOtherEditor;
  176. }
  177. private void ConfigureCodePopupEditor(CodePopupEditorControl popup, DynamicGridColumn column, CodePopupEditor editor)
  178. {
  179. popup.ColumnName = column.ColumnName;
  180. LoadLookupColumns(column, popup.OtherColumns);
  181. if (popup.EditorDefinition is DataLookupEditor dataLookup)
  182. LoadLookupColumns(column, dataLookup.OtherColumns);
  183. popup.CodeColumn = !string.IsNullOrEmpty(editor.CodeColumn) ? editor.CodeColumn : GetCodeColumn(column);
  184. popup.OnDefineFilter += (sender, type) => { return OnDefineFilter?.Invoke(sender, type); };
  185. popup.OnUpdateOtherEditor += Lookup_OnUpdateOtherEditor;
  186. }
  187. private void ConfigureLookupEditor(LookupEditorControl lookup, DynamicGridColumn column, LookupEditor editor)
  188. {
  189. if (editor.LookupWidth != int.MaxValue)
  190. lookup.Width = editor.LookupWidth;
  191. lookup.ColumnName = column.ColumnName;
  192. LoadLookupColumns(column, lookup.OtherColumns);
  193. if (lookup.EditorDefinition is DataLookupEditor dataLookup)
  194. LoadLookupColumns(column, dataLookup.OtherColumns);
  195. lookup.OnUpdateOtherEditor += Lookup_OnUpdateOtherEditor;
  196. lookup.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
  197. lookup.OnLookupsDefined += sender => { OnLookupsDefined?.Invoke(sender); };
  198. }
  199. private void ConfigureEnumEditor(LookupEditorControl lookup, DynamicGridColumn column, EnumLookupEditor editor)
  200. {
  201. if (editor.LookupWidth != int.MaxValue)
  202. lookup.Width = editor.LookupWidth;
  203. lookup.ColumnName = column.ColumnName;
  204. lookup.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
  205. lookup.OnLookupsDefined += sender =>
  206. {
  207. //OnLookupsDefined?.Invoke(sender);
  208. };
  209. }
  210. private void ConfigureComboEditor(LookupEditorControl lookup, DynamicGridColumn column, ComboLookupEditor editor)
  211. {
  212. if (editor.LookupWidth != int.MaxValue)
  213. lookup.Width = editor.LookupWidth;
  214. lookup.ColumnName = column.ColumnName;
  215. lookup.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
  216. lookup.OnLookupsDefined += sender => { OnLookupsDefined?.Invoke(sender); };
  217. }
  218. private void ConfigureMultiLookupEditor(MultiLookupEditorControl lookup, DynamicGridColumn column, ComboMultiLookupEditor editor)
  219. {
  220. if (editor.LookupWidth != int.MaxValue)
  221. lookup.Width = editor.LookupWidth;
  222. lookup.ColumnName = column.ColumnName;
  223. lookup.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
  224. lookup.OnLookupsDefined += sender => { OnLookupsDefined?.Invoke(sender); };
  225. }
  226. private void ConfigureCheckListEditor(CheckListBoxEditorControl checks, DynamicGridColumn column, CheckListEditor editor)
  227. {
  228. checks.Width = editor.LookupWidth;
  229. checks.ColumnName = column.ColumnName;
  230. checks.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
  231. checks.OnLookupsDefined += sender => { OnLookupsDefined?.Invoke(sender); };
  232. }
  233. private void ConfigureDocumentEditor(DocumentEditorControl document, DynamicGridColumn column, BaseDocumentEditor editor)
  234. {
  235. document.ColumnName = column.ColumnName;
  236. LoadLookupColumns(column, document.OtherColumns);
  237. if (document.EditorDefinition is DataLookupEditor dataLookup)
  238. LoadLookupColumns(column, dataLookup.OtherColumns);
  239. document.OnGetDocument += id => { return OnGetDocument?.Invoke(id); };
  240. document.OnSaveDocument += doc => { OnSaveDocument?.Invoke(doc); };
  241. document.OnFindDocument += file => { return OnFindDocument?.Invoke(file); };
  242. document.OnUpdateOtherEditor += Lookup_OnUpdateOtherEditor;
  243. document.Filter = editor.FileMask;
  244. }
  245. private void Lookup_OnUpdateOtherEditor(string columnname, object value)
  246. {
  247. var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
  248. if (editor != null)
  249. CoreUtils.SetPropertyValue(editor, "Value", value);
  250. }
  251. private void ConfigurePasswordEditor(PasswordEditorControl passwordEditorControl, DynamicGridColumn column, PasswordEditor passwordEditor)
  252. {
  253. passwordEditorControl.ViewButtonVisible = passwordEditor.ViewButtonVisible;
  254. }
  255. //private IEnumerable<BaseDynamicEditorControl> FindEditors(FrameworkElement element, DynamicGridColumn column)
  256. //{
  257. // if (element == null)
  258. // return new List<BaseDynamicEditorControl>();
  259. // if ((element is Border) && ((element as Border).Child is ScrollViewer))
  260. // return FindEditors(((element as Border).Child as ScrollViewer), column);
  261. // return element.FindVisualChildren<BaseDynamicEditorControl>().Where(x => x.ColumnName != null && x.ColumnName.Equals(column.ColumnName));
  262. //}
  263. private IEnumerable<BaseDynamicEditorControl> FindEditors(DynamicGridColumn column)
  264. {
  265. var results = new List<BaseDynamicEditorControl>();
  266. foreach (DynamicTabItem tab in Details.Items)
  267. {
  268. var border = tab.Content as Border;
  269. if (border != null)
  270. {
  271. var scroll = border.Child as ScrollViewer;
  272. if (scroll != null)
  273. {
  274. var grid = scroll.Content as Grid;
  275. if (grid != null)
  276. results.AddRange(grid.Children.OfType<BaseDynamicEditorControl>()
  277. .Where(x => string.Equals(x.ColumnName, column.ColumnName)));
  278. }
  279. }
  280. }
  281. //results.AddRange(FindEditors(tab.Content as FrameworkElement, column));
  282. return results;
  283. }
  284. private void ConfigureEditors()
  285. {
  286. if (editors.Any() && _columns != null)
  287. foreach (var column in _columns)
  288. {
  289. var editorname = column.ColumnName.Replace(".", "_");
  290. var
  291. Editors = FindEditors(
  292. column); //Details.FindVisualChildren<BaseDynamicEditorControl>().Where(x => x.ColumnName != null && x.ColumnName.Equals(column.ColumnName));
  293. foreach (var Editor in Editors)
  294. {
  295. var editor = Editor.EditorDefinition;
  296. //List<Object> parameters = editor.Parameters != null ? editor.Parameters.ToList() : new List<object>();
  297. if (Editor is LookupEditorControl lookupControl)
  298. {
  299. if (editor is LookupEditor lookupEditor)
  300. ConfigureLookupEditor(lookupControl, column, lookupEditor);
  301. else if (editor is EnumLookupEditor enumEditor)
  302. ConfigureEnumEditor(lookupControl, column, enumEditor);
  303. else if (editor is ComboLookupEditor comboEditor)
  304. ConfigureComboEditor(lookupControl, column, comboEditor);
  305. }
  306. else if(Editor is MultiLookupEditorControl multiLookupEditor && editor is ComboMultiLookupEditor comboMultiLookup)
  307. {
  308. ConfigureMultiLookupEditor(multiLookupEditor, column, comboMultiLookup);
  309. }
  310. else if (Editor is CheckListBoxEditorControl checkBoxControl && editor is CheckListEditor checkListEditor)
  311. {
  312. ConfigureCheckListEditor(checkBoxControl, column, checkListEditor);
  313. }
  314. else if (Editor is PopupEditorControl popupControl && editor is PopupEditor popupEditor)
  315. {
  316. ConfigurePopupEditor(popupControl, column, popupEditor);
  317. }
  318. else if (Editor is CodePopupEditorControl codePopupControl && editor is CodePopupEditor codePopupEditor)
  319. {
  320. ConfigureCodePopupEditor(codePopupControl, column, codePopupEditor);
  321. }
  322. else if (Editor is DocumentEditorControl documentEditorControl && editor is BaseDocumentEditor baseDocumentEditor)
  323. {
  324. ConfigureDocumentEditor(documentEditorControl, column, baseDocumentEditor);
  325. }
  326. else if (Editor is PasswordEditorControl passwordEditorControl && editor is PasswordEditor passwordEditor)
  327. {
  328. ConfigurePasswordEditor(passwordEditorControl, column, passwordEditor);
  329. }
  330. else if(Editor is ExpressionEditorControl expressionEditorControl && editor is ExpressionEditor expressionEditor)
  331. {
  332. ConfigureExpressionEditor(expressionEditorControl);
  333. }
  334. Editor.Configure();
  335. if (!editors.Any(x => x.ColumnName.Equals(Editor.ColumnName)))
  336. editors.Add(Editor);
  337. Editor.Loaded = true;
  338. }
  339. }
  340. }
  341. public virtual void DoReconfigureEditors()
  342. {
  343. ReconfigureEditors?.Invoke(this);
  344. }
  345. #endregion
  346. #region Editor
  347. private void EditorValueChanged(IDynamicEditorControl sender, Dictionary<string, object> values)
  348. {
  349. //Logger.Send(LogType.Information, "", string.Format("DynamicEditorGrid.EditorValueChanged({0})", values.Keys.Count));
  350. var changededitors = new Dictionary<string, object?>();
  351. foreach (var key in values.Keys)
  352. {
  353. var changedcolumns = OnEditorValueChanged?.Invoke(this, key, values[key]);
  354. if (changedcolumns != null)
  355. foreach (var (change, value) in changedcolumns)
  356. if (editors.Any(x => x.ColumnName.Equals(change)) && !changededitors.ContainsKey(change) && !change.Equals(sender.ColumnName))
  357. changededitors[change] = value;
  358. }
  359. if (changededitors.Any())
  360. LoadEditorValues(changededitors);
  361. DoReconfigureEditors();
  362. }
  363. private void LoadEditorValues(Dictionary<string, object?>? changededitors = null)
  364. {
  365. var columnnames = changededitors != null ? changededitors.Keys.ToArray() : editors.Select(x => x.ColumnName).ToArray();
  366. foreach (var columnname in columnnames)
  367. {
  368. var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
  369. if (editor == null)
  370. continue;
  371. var bLoaded = editor.Loaded;
  372. editor.Loaded = false;
  373. if (changededitors != null && changededitors.ContainsKey(columnname))
  374. {
  375. CoreUtils.SetPropertyValue(editor, "Value", changededitors[columnname]);
  376. }
  377. else
  378. {
  379. var curvalue = OnGetPropertyValue?.Invoke(this, columnname);
  380. try
  381. {
  382. CoreUtils.SetPropertyValue(editor, "Value", curvalue);
  383. }
  384. catch (Exception e)
  385. {
  386. MessageBox.Show($"Unable to set editor value for {columnname} -> {curvalue}: {CoreUtils.FormatException(e)}");
  387. }
  388. CoreUtils.SetPropertyValue(editor, "Changed", false);
  389. }
  390. editor.Loaded = bLoaded;
  391. editor.OnEditorValueChanged += EditorValueChanged;
  392. }
  393. }
  394. #endregion
  395. #region Loading + Editing Layout
  396. private bool LoadLayout(string xaml)
  397. {
  398. if (!string.IsNullOrWhiteSpace(xaml))
  399. try
  400. {
  401. IsCustomLayout = true;
  402. Content = null;
  403. Details = (XamlReader.Parse(xaml) as DynamicTabControl) ?? throw new Exception("XAML is not a DynamicTabControl");
  404. Content = Details;
  405. Details.ApplyTemplate();
  406. var iHeight = Details.Height > 0 ? Details.Height : 600;
  407. var iWidth = Details.Width > 0 ? Details.Width : 800;
  408. Details.Height = double.NaN;
  409. Details.Width = double.NaN;
  410. OnEditorCreated?.Invoke(this, iHeight, iWidth);
  411. return true;
  412. }
  413. catch (Exception e)
  414. {
  415. MessageBox.Show(string.Format("Unable to Load XAML!\n\n{0}", e.Message));
  416. }
  417. return false;
  418. }
  419. private decimal GetSequence(DynamicGridColumn column)
  420. {
  421. if (OnGetSequence != null)
  422. return OnGetSequence.Invoke(column);
  423. return 999;
  424. }
  425. private void CreateLayout()
  426. {
  427. //Stopwatch sw = new Stopwatch();
  428. //sw.Start();
  429. IsCustomLayout = false;
  430. Content = null;
  431. Details = new DynamicTabControl();
  432. Details.VerticalAlignment = VerticalAlignment.Stretch;
  433. Details.HorizontalAlignment = HorizontalAlignment.Stretch;
  434. Details.Name = "Details";
  435. //Logger.Send(LogType.Information, "DEG.CreateLayout", String.Format("Created Tab Control: {0}", sw.ElapsedMilliseconds));
  436. //sw.Restart();
  437. var EditorGrids = new Dictionary<string, Grid>();
  438. CustomGrid = EnsureGrid(EditorGrids, "Custom Fields");
  439. EditorGrid = EnsureGrid(EditorGrids, "General");
  440. //Logger.Send(LogType.Information, "DEG.CreateLayout", String.Format("Ensured Grids: {0}", sw.ElapsedMilliseconds));
  441. //sw.Restart();
  442. Details.SelectionChanged += Details_SelectionChanged;
  443. Content = Details;
  444. editors.Clear();
  445. double fGeneralHeight = 30; // Allow for Tab Header
  446. //Logger.Send(LogType.Information, "DEG.CreateLayout", String.Format("Sorted Columns: {0}", sw.ElapsedMilliseconds));
  447. //sw.Restart();
  448. foreach (var column in _columns.OrderBy(x => GetSequence(x)))
  449. {
  450. var iProp = DatabaseSchema.Property(UnderlyingType, column.ColumnName);
  451. var editor = OnGetEditor?.Invoke(column);
  452. if (editor != null && iProp?.ShouldShowEditor() != true)
  453. {
  454. editor.Visible = Visible.Hidden;
  455. editor.Editable = Editable.Hidden;
  456. }
  457. if(editor is not null)
  458. {
  459. OnGridCustomiseEditor?.Invoke(this, column, editor);
  460. }
  461. if (editor != null && editor.Editable != Editable.Hidden)
  462. {
  463. var page = string.IsNullOrWhiteSpace(editor.Page) ? iProp is StandardProperty ? "General" : "Custom Fields" : editor.Page;
  464. var grid = EnsureGrid(EditorGrids, page);
  465. //List<Object> parameters = editor.Parameters != null ? editor.Parameters.ToList() : new List<object>();
  466. //bool bParams = true;
  467. BaseDynamicEditorControl? element = null;
  468. element = editor switch
  469. {
  470. TextBoxEditor => new TextBoxEditorControl(),
  471. Core.RichTextEditor => new RichTextEditorControl(),
  472. URLEditor => new URLEditorControl(),
  473. CodeEditor or UniqueCodeEditor => new CodeEditorControl(),
  474. CheckBoxEditor => new CheckBoxEditorControl(),
  475. DateTimeEditor => new DateTimeEditorControl(),
  476. DateEditor dateEditor => new DateEditorControl { TodayVisible = dateEditor.TodayVisible },
  477. TimeOfDayEditor => new TimeOfDayEditorControl { NowButtonVisible = false },
  478. DurationEditor => new DurationEditorControl(),
  479. NotesEditor => new NotesEditorControl(),
  480. PINEditor => new PINEditorControl(),
  481. CheckListEditor => new CheckListBoxEditorControl(),
  482. MemoEditor => new MemoEditorControl(),
  483. JsonEditor => new JsonEditorControl(),
  484. LookupEditor => ClientFactory.IsSupported(((LookupEditor)editor).Type) ? new LookupEditorControl() : null,
  485. PopupEditor => ClientFactory.IsSupported(((PopupEditor)editor).Type) ? new PopupEditorControl() : null,
  486. CodePopupEditor => ClientFactory.IsSupported(((CodePopupEditor)editor).Type) ? new CodePopupEditorControl() : null,
  487. EnumLookupEditor or ComboLookupEditor => new LookupEditorControl(),
  488. ComboMultiLookupEditor => new MultiLookupEditorControl(),
  489. EmbeddedImageEditor imageEditor => new EmbeddedImageEditorControl
  490. {
  491. MaximumHeight = imageEditor.MaximumHeight,
  492. MaximumWidth = imageEditor.MaximumWidth,
  493. MaximumFileSize = imageEditor.MaximumFileSize
  494. },
  495. FileNameEditor fileNameEditor => new FileNameEditorControl
  496. {
  497. Filter = fileNameEditor.FileMask,
  498. AllowView = fileNameEditor.AllowView,
  499. RequireExisting = fileNameEditor.RequireExisting
  500. },
  501. FolderEditor folderEditor => new FolderEditorControl
  502. {
  503. InitialFolder = folderEditor.InitialFolder
  504. },
  505. MiscellaneousDocumentEditor => new DocumentEditorControl(),
  506. ImageDocumentEditor => new DocumentEditorControl(),
  507. VectorDocumentEditor => new DocumentEditorControl(),
  508. PDFDocumentEditor => new DocumentEditorControl(),
  509. PasswordEditor => new PasswordEditorControl(),
  510. CurrencyEditor => new CurrencyEditorControl(),
  511. DoubleEditor => new DoubleEditorControl(),
  512. IntegerEditor => new IntegerEditorControl(),
  513. Core.ScriptEditor scriptEditor => new ScriptEditorControl
  514. {
  515. SyntaxLanguage = scriptEditor.SyntaxLanguage
  516. },
  517. TimestampEditor => new TimestampEditorControl(),
  518. ColorEditor => new ColorEditorControl(),
  519. FilterEditor filter => new FilterEditorControl { FilterType = filter.Type! },
  520. ExpressionEditor expression => new ExpressionEditorControl(expression),
  521. _ => null,
  522. };
  523. if (element != null)
  524. {
  525. element.EditorDefinition = editor; //22
  526. element.IsEnabled = editor.Editable == Editable.Enabled;
  527. if (!string.IsNullOrWhiteSpace(editor.ToolTip))
  528. {
  529. element.ToolTip = new ToolTip() { Content = editor.ToolTip };
  530. }
  531. var label = new Label();
  532. label.Content = CoreUtils.Neatify(editor.Caption); // 2
  533. label.Margin = new Thickness(0F, 0F, 0F, 0F);
  534. label.HorizontalAlignment = HorizontalAlignment.Stretch;
  535. label.VerticalAlignment = VerticalAlignment.Stretch;
  536. label.HorizontalContentAlignment = HorizontalAlignment.Left;
  537. label.VerticalContentAlignment = VerticalAlignment.Center;
  538. label.SetValue(Grid.RowProperty, grid.RowDefinitions.Count);
  539. label.SetValue(Grid.ColumnProperty, 0);
  540. label.Visibility = string.IsNullOrWhiteSpace(editor.Caption) ? Visibility.Collapsed : Visibility.Visible;
  541. grid.Children.Add(label);
  542. element.ColumnName = column.ColumnName;
  543. element.Color = editor is UniqueCodeEditor ? Color.FromArgb(0xFF, 0xF6, 0xC9, 0xE8) : Colors.LightYellow;
  544. editors.Add(element);
  545. element.Margin = new Thickness(5F, 2.5F, 5F, 2.5F);
  546. double iHeight = element.DesiredHeight();
  547. if (iHeight == int.MaxValue)
  548. {
  549. grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
  550. fGeneralHeight += grid == EditorGrid ? element.MinHeight + 5.0F : 0.0F;
  551. }
  552. else
  553. {
  554. grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(iHeight + 5.0F) });
  555. fGeneralHeight += grid == EditorGrid ? iHeight + 5.0F : 0.0F;
  556. }
  557. double iWidth = element.DesiredWidth();
  558. if (iWidth == int.MaxValue)
  559. {
  560. element.HorizontalAlignment = HorizontalAlignment.Stretch;
  561. }
  562. else
  563. {
  564. element.HorizontalAlignment = HorizontalAlignment.Left;
  565. element.Width = iWidth;
  566. }
  567. element.SetValue(Grid.RowProperty, grid.RowDefinitions.Count - 1);
  568. element.SetValue(Grid.ColumnProperty, 1);
  569. grid.Children.Add(element);
  570. }
  571. }
  572. }
  573. //Logger.Send(LogType.Information, "DEG.CreateLayout", String.Format("Created Editors: {0}", sw.ElapsedMilliseconds));
  574. //sw.Restart();
  575. OnEditorCreated?.Invoke(this, fGeneralHeight, 800);
  576. if(Details.Items[^1] is DynamicTabItem custom)
  577. {
  578. custom.Visibility = CustomGrid.Children.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
  579. }
  580. //Logger.Send(LogType.Information, "DEG.CreateLayout", String.Format("Finalised: {0}", sw.ElapsedMilliseconds));
  581. //sw.Stop();
  582. }
  583. private Grid EnsureGrid(Dictionary<string, Grid> grids, string caption)
  584. {
  585. if (grids.ContainsKey(caption))
  586. return grids[caption];
  587. // Create Editor, ScrollViewer and TabItem for Dynamic Editor
  588. var result = new Grid
  589. {
  590. HorizontalAlignment = HorizontalAlignment.Stretch,
  591. VerticalAlignment = VerticalAlignment.Stretch,
  592. //Background = new SolidColorBrush(Colors.Blue),
  593. Margin = new Thickness(0, 2.5, 0, 2.5)
  594. };
  595. result.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
  596. result.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
  597. var scroll = new ScrollViewer
  598. {
  599. HorizontalAlignment = HorizontalAlignment.Stretch,
  600. VerticalAlignment = VerticalAlignment.Stretch,
  601. VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
  602. Padding = new Thickness(2)
  603. };
  604. scroll.Content = result;
  605. var border = new Border
  606. {
  607. BorderBrush = new SolidColorBrush(Colors.Gray),
  608. Background = new SolidColorBrush(Colors.White),
  609. BorderThickness = new Thickness(0.75)
  610. };
  611. border.Child = scroll;
  612. var tab = new DynamicTabItem();
  613. tab.Header = caption;
  614. tab.Content = border;
  615. if (Details.Items.Count == 0)
  616. Details.Items.Add(tab);
  617. else
  618. Details.Items.Insert(Details.Items.Count - 1, tab);
  619. grids[caption] = result;
  620. return result;
  621. }
  622. private string FormatXML(string xml)
  623. {
  624. var result = "";
  625. var mStream = new MemoryStream();
  626. var writer = new XmlTextWriter(mStream, Encoding.Unicode);
  627. var document = new XmlDocument();
  628. try
  629. {
  630. // Load the XmlDocument with the XML.
  631. document.LoadXml(xml);
  632. writer.Formatting = Formatting.Indented;
  633. // Write the XML into a formatting XmlTextWriter
  634. document.WriteContentTo(writer);
  635. writer.Flush();
  636. mStream.Flush();
  637. // Have to rewind the MemoryStream in order to read
  638. // its contents.
  639. mStream.Position = 0;
  640. // Read MemoryStream contents into a StreamReader.
  641. var sReader = new StreamReader(mStream);
  642. // Extract the text from the StreamReader.
  643. var formattedXml = sReader.ReadToEnd();
  644. result = formattedXml;
  645. }
  646. catch (XmlException)
  647. {
  648. // Handle the exception
  649. }
  650. mStream.Close();
  651. writer.Close();
  652. return result;
  653. }
  654. public void EditLayout()
  655. {
  656. ClearPages();
  657. //UnloadEditorValues();
  658. var xaml = new GlobalConfiguration<ScreenLayout>(_layoutname).Load().XAML;
  659. if (string.IsNullOrWhiteSpace(xaml))
  660. {
  661. Details.Height = Details.ActualHeight;
  662. Details.Width = Details.ActualWidth;
  663. //xaml = XamlWriter.Save(GetParentWindow(Details));
  664. xaml = XamlWriter.Save(Details);
  665. }
  666. xaml = FormatXML(xaml);
  667. var scripteditor = new ScriptEditor(xaml, SyntaxLanguage.XAML);
  668. if (scripteditor.ShowDialog() == true)
  669. {
  670. var layout = new ScreenLayout { XAML = scripteditor.Script };
  671. new GlobalConfiguration<ScreenLayout>(_layoutname).Save(layout);
  672. Content = null;
  673. if (!LoadLayout(layout.XAML))
  674. if (!LoadLayout(xaml))
  675. CreateLayout();
  676. Details.ApplyTemplate();
  677. Application.Current.Dispatcher.Invoke(() =>
  678. {
  679. ConfigureEditors();
  680. LoadEditorValues();
  681. AddPages();
  682. DoReconfigureEditors();
  683. }, DispatcherPriority.Render);
  684. }
  685. }
  686. internal void ResetLayout()
  687. {
  688. new GlobalConfiguration<ScreenLayout>(_layoutname).Delete();
  689. ClearPages();
  690. //UnloadEditorValues();
  691. CreateLayout();
  692. LoadEditorValues();
  693. AddPages();
  694. DoReconfigureEditors();
  695. }
  696. #endregion
  697. #region Pages
  698. //List<TabItem> configuredpages = new List<TabItem>();
  699. private void Details_SelectionChanged(object sender, SelectionChangedEventArgs e)
  700. {
  701. if (bChanging || Details?.SelectedItem == null || e.OriginalSource != Details)
  702. return;
  703. bChanging = true;
  704. try
  705. {
  706. var tab = Details.SelectedItem as DynamicTabItem;
  707. if(tab is not null)
  708. {
  709. var page = tab.Content as IDynamicEditorPage;
  710. if (page is not null)
  711. {
  712. if (!page.Ready)
  713. using (new WaitCursor())
  714. {
  715. OnLoadPage?.Invoke(page);
  716. }
  717. }
  718. else
  719. {
  720. if (!_loaded || e.RemovedItems.Count == 0 || e.AddedItems.Count == 0 || e.AddedItems?[0] == e.RemovedItems?[0])
  721. return;
  722. //if (!configuredpages.Contains(tab))
  723. //{
  724. // ConfigureEditors(eds);
  725. // configuredpages.Add(tab);
  726. //}
  727. var selectedGrid = ((tab.Content as Border)?.Child as ScrollViewer)?.Content;
  728. var eds = editors
  729. .Where(x => x is BaseDynamicEditorControl control &&
  730. control.Parent == selectedGrid)
  731. .Select(x => (BaseDynamicEditorControl)x);
  732. foreach (var ed in eds)
  733. {
  734. var editorvalue = ed.GetValue();
  735. var entityvalue = OnGetPropertyValue?.Invoke(this, ed.ColumnName);
  736. if (!Equals(editorvalue, entityvalue))
  737. {
  738. ed.Loaded = false;
  739. ed.SetValue(entityvalue);
  740. ed.Loaded = true;
  741. }
  742. }
  743. }
  744. OnSelectPage?.Invoke(tab, null);
  745. }
  746. }
  747. finally
  748. {
  749. bChanging = false;
  750. }
  751. }
  752. public void UnloadPages(bool saved)
  753. {
  754. if(Pages is not null)
  755. foreach (var page in Pages)
  756. if (page.Ready)
  757. OnUnloadPage?.Invoke(page, saved);
  758. }
  759. private void AddPages()
  760. {
  761. if (Pages != null)
  762. using (new WaitCursor())
  763. {
  764. foreach (var page in Pages.OrderBy(x => x.Order()).ThenBy(x => x.Caption()))
  765. {
  766. var tab = new DynamicTabItem();
  767. tab.Header = page.Caption();
  768. tab.Content = page;
  769. Details.Items.Insert(Details.Items.Count - 1, tab);
  770. pagemap[page] = tab;
  771. if (PreloadPages)
  772. OnLoadPage?.Invoke(page);
  773. page.EditorGrid = this;
  774. }
  775. }
  776. //if (Details.Items.Count <= 2)
  777. // ((TabItem)Details.Items[0]).Visibility = Visibility.Collapsed;
  778. }
  779. private void ClearPages()
  780. {
  781. foreach (var page in pagemap.Keys)
  782. {
  783. var tab = pagemap[page];
  784. tab.Content = null;
  785. Details.Items.Remove(tab);
  786. page.Ready = false;
  787. }
  788. pagemap.Clear();
  789. //if (_pages != null)
  790. //{
  791. // foreach (var page in _pages)
  792. // {
  793. // var tab = Details.Items.OfType<TabItem>().FirstOrDefault(x => x.Content.Equals(page));
  794. // if (tab != null)
  795. // {
  796. // tab.Content = null;
  797. // Details.Items.Remove(tab);
  798. // }
  799. // }
  800. //}
  801. }
  802. public void Load(string layoutname, DynamicEditorPages? pages = null)
  803. {
  804. _layoutname = layoutname;
  805. Pages = pages;
  806. //Stopwatch sw = new Stopwatch();
  807. //sw.Start();
  808. _columns = OnCustomiseColumns?.Invoke(this, null) ?? new DynamicGridColumns();
  809. //Logger.Send(LogType.Information, "DEG.Load", String.Format("Loaded Columns: {0}", sw.ElapsedMilliseconds));
  810. //sw.Restart();
  811. var layout = new GlobalConfiguration<ScreenLayout>(_layoutname).Load();
  812. //Logger.Send(LogType.Information, "DEG.Load", String.Format("Loaded Layout: {0}", sw.ElapsedMilliseconds));
  813. //sw.Restart();
  814. if (!LoadLayout(layout.XAML))
  815. CreateLayout();
  816. //Logger.Send(LogType.Information, "DEG.Load", String.Format("Created Layout: {0}", sw.ElapsedMilliseconds));
  817. //sw.Restart();
  818. }
  819. public bool Unload()
  820. {
  821. ClearPages();
  822. var bChanged = false;
  823. foreach (var columnname in changes.Keys)
  824. {
  825. OnSetPropertyValue?.Invoke(this, columnname, changes[columnname]);
  826. bChanged = true;
  827. }
  828. return bChanged;
  829. }
  830. #endregion
  831. protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  832. {
  833. base.OnRenderSizeChanged(sizeInfo);
  834. foreach (var columnname in editors.Select(x => x.ColumnName).ToArray())
  835. {
  836. var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
  837. if(editor is not null)
  838. {
  839. editor.Loaded = true;
  840. }
  841. }
  842. }
  843. }
  844. }