DynamicEditorGrid.xaml.cs 40 KB

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