EmbeddedDynamicEditorForm.xaml.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. using InABox.Core;
  2. using InABox.Wpf;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Linq;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. namespace InABox.DynamicGrid
  13. {
  14. /// <summary>
  15. /// Interaction logic for EmbeddedDynamicEditorForm.xaml
  16. /// </summary>
  17. public partial class EmbeddedDynamicEditorForm : UserControl, IDynamicEditorForm
  18. {
  19. public event OnBeforeLoad? OnBeforeLoad;
  20. public void BeforeLoad() => OnBeforeLoad?.Invoke(this);
  21. public event OnAfterLoad? OnAfterLoad;
  22. public void AfterLoad() => OnAfterLoad?.Invoke(this);
  23. public event EventHandler OnChanged;
  24. private bool bChanged = false;
  25. public void DoChanged()
  26. {
  27. bChanged = true;
  28. //OKButton.IsEnabled = true;
  29. //CancelButton.IsEnabled = true;
  30. OnChanged?.Invoke(this, EventArgs.Empty);
  31. }
  32. public delegate void OKEvent();
  33. public delegate void CancelEvent();
  34. public DynamicEditorPages Pages { get; private set; } = [];
  35. private BaseObject[] _items;
  36. public BaseObject[] Items
  37. {
  38. get => _items;
  39. set
  40. {
  41. _items = value;
  42. DynamicEditorFormModel.Slug = Items?.FirstOrDefault()?.GetType().EntityName().Split('.').Last() ?? "";
  43. Editor.Load(Pages);
  44. foreach (var page in Pages)
  45. page.OnChanged += (sender, args) => DoChanged();
  46. }
  47. }
  48. public bool ReadOnly
  49. {
  50. get => Editor.ReadOnly;
  51. set
  52. {
  53. Editor.ReadOnly = value;
  54. UpdateButtonEnabled();
  55. }
  56. }
  57. private bool _disableIfUnchanged = false;
  58. public bool DisableOKIfUnchanged
  59. {
  60. get => _disableIfUnchanged;
  61. set
  62. {
  63. _disableIfUnchanged = value;
  64. UpdateButtonEnabled();
  65. }
  66. }
  67. private bool _hideButtons = false;
  68. public bool HideButtons
  69. {
  70. get => _hideButtons;
  71. set
  72. {
  73. _hideButtons = value;
  74. OKButton.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
  75. CancelButton.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
  76. }
  77. }
  78. private bool _highlightButtons = false;
  79. public bool HighlightButtons
  80. {
  81. get => _highlightButtons;
  82. set
  83. {
  84. _highlightButtons = value;
  85. UpdateButtonHighlight(OKButton, Colors.DarkGreen, Colors.LimeGreen, Colors.White);
  86. UpdateButtonHighlight(CancelButton, Colors.Firebrick, Colors.Red, Colors.White);
  87. }
  88. }
  89. private void UpdateButtonEnabled()
  90. {
  91. OKButton.IsEnabled = !ReadOnly && (!DisableOKIfUnchanged || bChanged);
  92. CancelButton.IsEnabled = !ReadOnly;
  93. }
  94. private void UpdateButtonHighlight(Button button, Color border, Color background, Color foreground)
  95. {
  96. button.BorderBrush = _highlightButtons
  97. ? new SolidColorBrush(border)
  98. : new SolidColorBrush(Colors.Gray);
  99. button.Background = _highlightButtons
  100. ? new SolidColorBrush(background)
  101. : new SolidColorBrush(Colors.Gainsboro);
  102. button.Foreground = _highlightButtons
  103. ? new SolidColorBrush(foreground)
  104. : new SolidColorBrush(Colors.Black);
  105. button.FontWeight = _highlightButtons
  106. ? FontWeights.Bold
  107. : FontWeights.Normal;
  108. }
  109. public static readonly DependencyProperty ButtonsVisibleProperty =
  110. DependencyProperty.Register(
  111. nameof(ButtonsVisible),
  112. typeof(bool),
  113. typeof(EmbeddedDynamicEditorForm),
  114. new UIPropertyMetadata(true)
  115. );
  116. public bool ButtonsVisible
  117. {
  118. get => (bool)GetValue(ButtonsVisibleProperty);
  119. set
  120. {
  121. SetValue(ButtonsVisibleProperty, value);
  122. UpdateButtonsRowVisibility();
  123. }
  124. }
  125. private void UpdateButtonsRowVisibility()
  126. {
  127. ButtonRow.Height = ButtonsVisible
  128. ? new GridLength(1, GridUnitType.Auto)
  129. : new GridLength(0, GridUnitType.Pixel);
  130. }
  131. public static readonly DependencyProperty TabsVisibleProperty =
  132. DependencyProperty.Register(
  133. nameof(TabsVisible),
  134. typeof(bool),
  135. typeof(EmbeddedDynamicEditorForm),
  136. new UIPropertyMetadata(true)
  137. );
  138. public bool TabsVisible
  139. {
  140. get => (bool)GetValue(TabsVisibleProperty);
  141. set
  142. {
  143. SetValue(TabsVisibleProperty, value);
  144. UpdateTabsVisibility();
  145. }
  146. }
  147. private void UpdateTabsVisibility()
  148. {
  149. Editor.TabStripVisible = TabsVisible;
  150. }
  151. #region Events
  152. public event OnValidateData? OnValidateData;
  153. public OnCustomiseColumns? OnCustomiseColumns { get; set; }
  154. public OnDefineLookupFilter? OnDefineFilter { get; set; }
  155. public OnDefineLookup? OnDefineLookups { get; set; }
  156. public DefineEditorEventHandler? OnDefineEditor { get; set; }
  157. public event OnFormCustomiseEditor? OnFormCustomiseEditor;
  158. public OnReconfigureEditors? OnReconfigureEditors { get; set; }
  159. public event EditorValueChangedHandler? OnEditorValueChanged;
  160. public event OnAfterEditorValueChanged? OnAfterEditorValueChanged;
  161. public event OnSelectPage? OnSelectPage;
  162. public DynamicGridSaveEvent? OnSaveItem { get; set; }
  163. public DynamicEditorGrid.EditorCreatedHandler? OnEditorCreated;
  164. public event OKEvent? OnOK;
  165. public event CancelEvent? OnCancel;
  166. #endregion
  167. public EmbeddedDynamicEditorForm()
  168. {
  169. InitializeComponent();
  170. ReadOnly = false;
  171. }
  172. public override void OnApplyTemplate()
  173. {
  174. base.OnApplyTemplate();
  175. UpdateButtonsRowVisibility();
  176. UpdateTabsVisibility();
  177. }
  178. public EmbeddedDynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
  179. Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false): this()
  180. {
  181. Setup(type, pages, buttons, PageDataHandler, PreloadPages);
  182. }
  183. private IFilter? Editor_OnDefineFilter(Type type, string column)
  184. {
  185. return OnDefineFilter?.Invoke(type, column);
  186. }
  187. private void ClearEvents()
  188. {
  189. OnEditorValueChanged = null;
  190. OnAfterEditorValueChanged = null;
  191. OnSelectPage = null;
  192. OnValidateData = null;
  193. }
  194. public void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
  195. Func<Type, CoreTable?>? PageDataHandler = null, bool PreloadPages = false)
  196. {
  197. ClearEvents();
  198. Editor.UnderlyingType = type;
  199. Editor.OnLoadPage = page => { page.Load(Items.First(), PageDataHandler); };
  200. Editor.PreloadPages = PreloadPages;
  201. Pages = pages ?? new DynamicEditorPages();
  202. Buttons.Children.Clear();
  203. if (buttons != null)
  204. foreach (var button in buttons)
  205. {
  206. var btn = new Button();
  207. UpdateButton(btn, button.Image, button.Name);
  208. btn.Tag = button;
  209. btn.Margin = new Thickness(0, 0, 5, 0);
  210. btn.Padding = new Thickness(5, 0, 5, 0);
  211. btn.Click += Btn_Click;
  212. Buttons.Children.Add(btn);
  213. button.Button = btn;
  214. button.Form = this;
  215. }
  216. }
  217. public void UnloadEditorPages(bool saved)
  218. {
  219. Editor.UnloadPages(saved);
  220. }
  221. protected void UpdateButton(Button button, BitmapImage? image, string text)
  222. {
  223. var stackPnl = new StackPanel();
  224. stackPnl.Orientation = Orientation.Horizontal;
  225. //stackPnl.Margin = new Thickness(2);
  226. if (image != null)
  227. {
  228. var img = new Image();
  229. img.Source = image;
  230. img.Margin = new Thickness(2);
  231. stackPnl.Children.Add(img);
  232. }
  233. if (!string.IsNullOrEmpty(text))
  234. {
  235. var lbl = new Label();
  236. lbl.Content = text;
  237. lbl.VerticalAlignment = VerticalAlignment.Stretch;
  238. lbl.VerticalContentAlignment = VerticalAlignment.Center;
  239. lbl.Margin = new Thickness(2, 0, 5, 0);
  240. stackPnl.Children.Add(lbl);
  241. }
  242. button.Content = stackPnl;
  243. }
  244. private Dictionary<string, object?> EditorValueChanged(IDynamicEditorForm sender, string name, object value)
  245. {
  246. if (OnEditorValueChanged != null)
  247. return OnEditorValueChanged(sender, name, value);
  248. return DynamicGridUtils.UpdateEditorValue(_items, name, value);
  249. }
  250. private void Editor_OnEditorCreated(object sender, double height, double width)
  251. {
  252. OnEditorCreated?.Invoke(sender, height, width);
  253. Editor.VerticalAlignment = VerticalAlignment.Stretch;
  254. Editor.HorizontalAlignment = HorizontalAlignment.Stretch;
  255. }
  256. private void Editor_OnCustomiseColumns(object sender, DynamicGridColumns columns)
  257. {
  258. columns.Clear();
  259. if (_items != null && _items.Any())
  260. columns.ExtractColumns(_items.First().GetType());
  261. OnCustomiseColumns?.Invoke(this, columns);
  262. }
  263. private void Btn_Click(object sender, RoutedEventArgs e)
  264. {
  265. var button = (Button)sender;
  266. var deb = (DynamicEditorButton)button.Tag;
  267. deb.Click();
  268. }
  269. private void OKButton_Click(object sender, RoutedEventArgs e)
  270. {
  271. var errors = OnValidateData?.Invoke(this, Items);
  272. if (errors != null && errors.Any())
  273. {
  274. MessageWindow.ShowMessage(
  275. string.Format("The following errors have been found with your data!\nPlease correct them and try again.\n\n- {0}",
  276. string.Join("\n- ", errors)),
  277. "Validation Error");
  278. return;
  279. }
  280. OnOK?.Invoke();
  281. //OKButton.IsEnabled = false;
  282. //CancelButton.IsEnabled = false;
  283. // Don't Commit the changes here, because we want to refer back to thos changes when we save the item
  284. // to trigger specific processes in the database
  285. //Close();
  286. }
  287. private void CancelButton_Click(object sender, RoutedEventArgs e)
  288. {
  289. // However, if we cancel the edits, then we can safely revert the items back to their original (loaded) state
  290. foreach (var item in _items)
  291. item.CancelChanges();
  292. OnCancel?.Invoke();
  293. //OKButton.IsEnabled = false;
  294. //CancelButton.IsEnabled = false;
  295. //Close();
  296. }
  297. public void SaveItem(CancelEventArgs e)
  298. {
  299. OnSaveItem?.Invoke(this, e);
  300. }
  301. public bool TryFindEditor(string columnname, [NotNullWhen(true)] out IDynamicEditorControl? editor)
  302. {
  303. return Editor.TryFindEditor(columnname, out editor);
  304. }
  305. public IDynamicEditorControl FindEditor(string columnname)
  306. {
  307. return Editor.FindEditor(columnname);
  308. }
  309. public object? GetEditorValue(string columnName) => FindEditor(columnName).GetValue(columnName);
  310. public void SetEditorValue(string columnName, object? value) => FindEditor(columnName).SetValue(columnName, value);
  311. public void SetLayoutType<T>() where T : DynamicEditorGridLayout => Editor.SetLayoutType<T>();
  312. public void SetLayoutType(Type t) => Editor.SetLayoutType(t);
  313. private void Editor_OnSelectPage(DynamicEditorGrid sender, BaseObject[] items)
  314. {
  315. OnSelectPage?.Invoke(sender, items);
  316. }
  317. private void Editor_OnUnloadPage(IDynamicEditorPage page, bool saved)
  318. {
  319. if (!saved)
  320. page.BeforeSave(Items.First());
  321. else
  322. page.AfterSave(Items.First());
  323. }
  324. private Dictionary<string, object?>? Editor_OnAfterEditorValueChanged(DynamicEditorGrid sender, AfterEditorValueChangedArgs args)
  325. {
  326. if(args.ChangedValues.Count > 0)
  327. {
  328. DoChanged();
  329. }
  330. return OnAfterEditorValueChanged?.Invoke(sender, args);
  331. }
  332. private void Editor_OnReconfigureEditors(DynamicEditorGrid sender)
  333. {
  334. OnReconfigureEditors?.Invoke(sender);
  335. }
  336. private void Editor_OnGridCustomiseEditor(DynamicEditorGrid sender, DynamicGridColumn column, BaseEditor editor)
  337. {
  338. OnFormCustomiseEditor?.Invoke(this, Items, column, editor);
  339. }
  340. private decimal Editor_OnGetSequence(DynamicGridColumn column)
  341. {
  342. if (_items.Any())
  343. return CoreUtils.GetPropertySequence(_items.First().GetType(), column.ColumnName);
  344. return 0.0M;
  345. }
  346. private void Editor_OnDefineLookups(ILookupEditorControl editor)
  347. {
  348. OnDefineLookups?.Invoke(editor);
  349. }
  350. private BaseObject[] Editor_GetItems()
  351. {
  352. return _items;
  353. }
  354. private BaseEditor Editor_OnGetEditor(DynamicGridColumn column)
  355. {
  356. if (_items != null && _items.Any())
  357. {
  358. var property = DatabaseSchema.Property(Editor.UnderlyingType, column.ColumnName);
  359. if (property == null) return new NullEditor();
  360. if (property.Editor is NullEditor)
  361. return property.Editor;
  362. BaseEditor editor;
  363. if (property is CustomProperty)
  364. {
  365. editor = property.Editor.CloneEditor();
  366. }
  367. else
  368. {
  369. editor = OnDefineEditor?.Invoke(_items[0], column) ?? column.Editor.CloneEditor();
  370. var propEditor = property.Editor;
  371. editor.Page = propEditor.Page;
  372. editor.Caption = propEditor.Caption;
  373. }
  374. //if (ReadOnly && editor.Editable.IsEditable())
  375. // editor.Editable = Editable.Disabled;
  376. return editor;
  377. }
  378. return new NullEditor();
  379. }
  380. private object? Editor_OnGetPropertyValue(object sender, string column)
  381. {
  382. if (!_items.Any())
  383. return null;
  384. object? result;
  385. try
  386. {
  387. result = CoreUtils.GetPropertyValue(_items.First(), column);
  388. }
  389. catch
  390. {
  391. result = _items.First().UserProperties.ContainsKey(column) ? _items.First().UserProperties[column] : null;
  392. }
  393. if (result == null)
  394. return null;
  395. foreach (var _item in _items)
  396. {
  397. object? curvalue;
  398. try
  399. {
  400. curvalue = CoreUtils.GetPropertyValue(_item, column);
  401. }
  402. catch
  403. {
  404. curvalue = _item.UserProperties.ContainsKey(column) ? _item.UserProperties[column] : null;
  405. }
  406. if (curvalue == null)
  407. return null;
  408. if (!curvalue.Equals(result))
  409. return null;
  410. }
  411. return result;
  412. }
  413. private void Editor_OnSetPropertyValue(object sender, string column, object value)
  414. {
  415. foreach (var _item in _items)
  416. if (_item.UserProperties.ContainsKey(column))
  417. _item.UserProperties[column] = value;
  418. else
  419. CoreUtils.SetPropertyValue(_item, column, value);
  420. }
  421. //public void EditLayout() => Editor.EditLayout();
  422. //public void ResetLayout() => Editor.ResetLayout();
  423. public void AddButton(Button button)
  424. {
  425. Buttons.Children.Add(button);
  426. }
  427. private Dictionary<string, object?> Editor_OnEditorValueChanged(DynamicEditorGrid sender, string name, object value)
  428. {
  429. return EditorValueChanged(this, name, value);
  430. }
  431. }
  432. }