UtilityDashboard.xaml.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Media;
  8. using Comal.Classes;
  9. using InABox.Configuration;
  10. using InABox.Core;
  11. using InABox.DynamicGrid;
  12. using InABox.WPF;
  13. using InABox.WPF.Themes;
  14. using Microsoft.Xaml.Behaviors.Core;
  15. using Syncfusion.Windows.Tools.Controls;
  16. namespace PRSDesktop
  17. {
  18. public class DashboardFavourite : BaseObject
  19. {
  20. [TextBoxEditor]
  21. [EditorSequence(1)]
  22. public string Name { get; set; }
  23. [CheckBoxEditor]
  24. [EditorSequence(2)]
  25. public bool IsGlobal { get; set; }
  26. [NullEditor]
  27. public string Layout { get; set; }
  28. }
  29. public class DynamicItemsListGrid<T> : DynamicGrid<T>
  30. where T : BaseObject, new()
  31. {
  32. public List<T> Items { get; set; }
  33. public DynamicItemsListGrid() : this(new()) { }
  34. public DynamicItemsListGrid(List<T> items) : base()
  35. {
  36. Items = items;
  37. }
  38. protected override void DeleteItems(params CoreRow[] rows)
  39. {
  40. foreach (var row in rows.OrderByDescending(x => x.Index))
  41. {
  42. Items.RemoveAt(row.Index);
  43. }
  44. }
  45. protected override T LoadItem(CoreRow row)
  46. {
  47. return Items[row.Index];
  48. }
  49. protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T>? sort, Action<CoreTable?, Exception?> action)
  50. {
  51. var result = new CoreTable();
  52. result.LoadColumns(typeof(T));
  53. result.LoadRows(Items);
  54. action.Invoke(result, null);
  55. }
  56. protected override void SaveItem(T item)
  57. {
  58. if (!Items.Contains(item))
  59. {
  60. Items.Add(item);
  61. }
  62. }
  63. }
  64. public class GlobalUtilityDashboardSettings : GlobalConfigurationSettings
  65. {
  66. public List<DashboardFavourite> Favourites { get; set; }
  67. public GlobalUtilityDashboardSettings()
  68. {
  69. Favourites = new();
  70. }
  71. }
  72. public class UtilityDashboardSettings : UserConfigurationSettings
  73. {
  74. public UtilityDashboardSettings()
  75. {
  76. Dashboards = new Dictionary<string, string>();
  77. Favourites = new();
  78. }
  79. public Dictionary<string, string> Dashboards { get; set; }
  80. public List<DashboardFavourite> Favourites { get; set; }
  81. public int Selected { get; set; }
  82. public bool AutoHide { get; set; }
  83. }
  84. /// <summary>
  85. /// Interaction logic for UtilityDashboard.xaml
  86. /// </summary>
  87. public partial class UtilityDashboard : UserControl, IBasePanel
  88. {
  89. private readonly Dictionary<string, DynamicFormDesignGrid> _dashboards = new();
  90. private readonly Dictionary<DynamicFormDesignGrid, List<ICorePanel>> _panels = new();
  91. private class WidgetDashboardElement
  92. {
  93. public Type DashboardElement { get; set; }
  94. public Type Widget { get; set; }
  95. public Type Group { get; set; }
  96. public Type Properties { get; set; }
  97. public string GroupCaption { get; set; }
  98. public string WidgetCaption { get; set; }
  99. public Type[] SecurityTokens { get; set; }
  100. public WidgetDashboardElement(Type dashboardElement, Type widget, Type group, Type properties, Type[] securityTokens)
  101. {
  102. DashboardElement = dashboardElement;
  103. Widget = widget;
  104. Group = group;
  105. Properties = properties;
  106. SecurityTokens = securityTokens;
  107. GroupCaption = UtilityDashboard.GetCaption(group);
  108. WidgetCaption = UtilityDashboard.GetCaption(widget);
  109. }
  110. }
  111. private static List<WidgetDashboardElement>? _dashboardElements;
  112. private string CurrentDashboardName => ((DashboardsTab.SelectedItem as DynamicTabItem)?.Header?.ToString()) ?? string.Empty;
  113. private DynamicFormDesignGrid? CurrentDashboard => CurrentDashboardName != null ? _dashboards.GetValueOrDefault(CurrentDashboardName) : null;
  114. private UtilityDashboardSettings _settings = new();
  115. public UtilityDashboard()
  116. {
  117. InitializeComponent();
  118. }
  119. public void CreateToolbarButtons(IPanelHost host)
  120. {
  121. }
  122. private void SaveSettings()
  123. {
  124. new UserConfiguration<UtilityDashboardSettings>().Save(_settings);
  125. }
  126. #region Panel Functions & Properties
  127. public event DataModelUpdateEvent? OnUpdateDataModel;
  128. public bool IsReady { get; set; }
  129. public string SectionName => "Utility Dashboard";
  130. public DataModel DataModel(Selection selection)
  131. {
  132. return new EmptyDataModel();
  133. }
  134. public void Setup()
  135. {
  136. _settings = new UserConfiguration<UtilityDashboardSettings>().Load();
  137. if (_settings.Dashboards.Count == 0) _settings.Dashboards["New Dashboard"] = CreateForm("").SaveLayout();
  138. foreach (var key in _settings.Dashboards.Keys)
  139. CreateTab(key);
  140. if (_settings.Selected >= -1 && _settings.Selected < DashboardsTab.Items.Count)
  141. DashboardsTab.SelectedIndex = _settings.Selected;
  142. //DashboardsTab.FullScreenMode = _settings.AutoHide ? FullScreenMode.ControlMode : FullScreenMode.None;
  143. }
  144. public Dictionary<string, object[]> Selected()
  145. {
  146. return new Dictionary<string, object[]>();
  147. }
  148. public void Heartbeat(TimeSpan time)
  149. {
  150. }
  151. public void Refresh()
  152. {
  153. RefreshDashboard(CurrentDashboardName);
  154. }
  155. public void Shutdown()
  156. {
  157. foreach (var (name, grid) in _dashboards)
  158. {
  159. ShutdownDashboard(name, grid);
  160. }
  161. _panels.Clear();
  162. }
  163. #endregion
  164. #region Favourites
  165. private IEnumerable<DashboardFavourite> GetFavourites()
  166. {
  167. foreach(var favourite in _settings.Favourites)
  168. {
  169. yield return favourite;
  170. }
  171. var global = new GlobalConfiguration<GlobalUtilityDashboardSettings>().Load().Favourites;
  172. foreach (var favourite in global)
  173. {
  174. yield return favourite;
  175. }
  176. }
  177. private void ManageFavourites_Click()
  178. {
  179. var favourites = GetFavourites().ToList();
  180. var grid = new DynamicItemsListGrid<DashboardFavourite>(favourites);
  181. grid.Options.AddRange(DynamicGridOption.DeleteRows, DynamicGridOption.EditRows, DynamicGridOption.MultiSelect);
  182. grid.OnCustomiseEditor += FavouritesGrid_OnCustomiseEditor;
  183. DynamicGridUtils.CreateGridWindow("Manage Favourites", grid).ShowDialog();
  184. _settings.Favourites = favourites.Where(x => !x.IsGlobal).ToList();
  185. SaveSettings();
  186. if (Security.IsAllowed<CanSetGlobalDashboardFavourites>())
  187. {
  188. var config = new GlobalConfiguration<GlobalUtilityDashboardSettings>();
  189. var global = config.Load();
  190. global.Favourites = favourites.Where(x => x.IsGlobal).ToList();
  191. config.Save(global);
  192. }
  193. }
  194. private void FavouritesGrid_OnCustomiseEditor(IDynamicEditorForm sender, DashboardFavourite[]? items, DynamicGridColumn column, BaseEditor editor)
  195. {
  196. if(column.ColumnName == "IsGlobal")
  197. {
  198. editor.Editable = Security.IsAllowed<CanSetGlobalDashboardFavourites>() ? Editable.Enabled : Editable.Disabled;
  199. }
  200. }
  201. private void SaveAsFavourite_Click(string dashboardName)
  202. {
  203. _settings.Favourites.Add(new DashboardFavourite
  204. {
  205. Name = dashboardName,
  206. Layout = _dashboards.GetValueOrDefault(dashboardName)?.Form.SaveLayout() ?? _settings.Dashboards[dashboardName]
  207. });
  208. SaveSettings();
  209. }
  210. private void LoadFavourite_Click(DashboardFavourite favourite)
  211. {
  212. var name = CreateNewTabName(favourite.Name);
  213. _settings.Dashboards[name] = favourite.Layout;
  214. SaveSettings();
  215. var tab = CreateTab(name);
  216. DashboardsTab.SelectedItem = tab;
  217. }
  218. #endregion
  219. #region Tabs
  220. private void Tab_OnContextMenuOpening(object sender, DynamicTabItemContextMenuEventArgs args)
  221. {
  222. var name = (DashboardsTab.SelectedItem as DynamicTabItem)?.Header?.ToString();
  223. if (string.IsNullOrEmpty(name))
  224. return;
  225. DynamicFormDesignGrid grid = _dashboards[name];
  226. var menu = args.Menu;
  227. menu.AddSeparatorIfNeeded();
  228. var isDesigning = grid.Mode != FormMode.Preview;
  229. menu.Items.Add(new MenuItem()
  230. {
  231. Header = isDesigning ? "Close Design Mode" : "Design Mode",
  232. Command = new ActionCommand(() =>
  233. {
  234. if (grid.Mode == FormMode.Designing)
  235. {
  236. grid.Mode = FormMode.Preview;
  237. SaveCurrentDashboard();
  238. DashboardsTab.ChangedCommand.Execute(null);
  239. }
  240. else
  241. {
  242. ShutdownDashboard();
  243. grid.Mode = FormMode.Designing;
  244. }
  245. }),
  246. Icon = new Image() { Source = (isDesigning ? PRSDesktop.Resources.delete : PRSDesktop.Resources.pencil).AsBitmapImage(24, 24) }
  247. });
  248. var index = 0;
  249. var favourites = GetFavourites().ToList();
  250. if (favourites.Any())
  251. {
  252. foreach (var favourite in favourites)
  253. {
  254. menu.AddItem(favourite.Name, null, favourite, LoadFavourite_Click, index: index++);
  255. }
  256. menu.AddSeparatorIfNeeded(index: index++);
  257. menu.AddItem("Manage Favourites", null, ManageFavourites_Click, index: index++);
  258. }
  259. menu.AddItem("Save as Favourite", null, name, SaveAsFavourite_Click, index: index++);
  260. menu.AddSeparator(index: index++);
  261. }
  262. private void Tab_OnCloseTab(object sender, DynamicTabControlEventArgs args)
  263. {
  264. var name = args.TabItem.Header?.ToString();
  265. if (name is null)
  266. return;
  267. _dashboards.Remove(name);
  268. _settings.Dashboards.Remove(name);
  269. if (!_settings.Dashboards.Any())
  270. {
  271. var tab = new DynamicTabItem();
  272. InitializeNewDashboardTab(tab);
  273. DashboardsTab.Items.Add(tab);
  274. }
  275. else
  276. {
  277. DashboardsTab.ChangedCommand.Execute(null);
  278. }
  279. }
  280. private void Tab_OnTabRenamed(object sender, DynamicTabItemRenamedEventArgs args)
  281. {
  282. var oldSettings = _settings.Dashboards[args.OldName];
  283. _settings.Dashboards.Remove(args.OldName);
  284. args.NewName = CreateNewTabName(args.NewName);
  285. if (_dashboards.TryGetValue(args.OldName, out var dashboard))
  286. {
  287. _dashboards.Remove(args.OldName);
  288. _dashboards[args.NewName] = dashboard;
  289. _settings.Dashboards[args.NewName] = dashboard.Form.SaveLayout();
  290. }
  291. else
  292. {
  293. _settings.Dashboards[args.NewName] = oldSettings;
  294. }
  295. }
  296. /// <summary>
  297. /// Setup events on a new tab.
  298. /// </summary>
  299. /// <param name="tab"></param>
  300. private void InitializeTab(DynamicTabItem tab)
  301. {
  302. tab.CanClose = true;
  303. tab.OnCloseTab += Tab_OnCloseTab;
  304. tab.CanRename = true;
  305. tab.OnTabRenamed += Tab_OnTabRenamed;
  306. tab.OnContextMenuOpening += Tab_OnContextMenuOpening;
  307. }
  308. private string CreateNewTabName(string name)
  309. {
  310. var newName = name;
  311. int i = 1;
  312. while (TabNameExists(newName))
  313. {
  314. newName = $"{name} ({i})";
  315. ++i;
  316. }
  317. return newName;
  318. }
  319. private bool TabNameExists(string name)
  320. {
  321. return _settings.Dashboards.ContainsKey(name);
  322. }
  323. /// <summary>
  324. /// Creates a new tab with a given header and adds it to <see cref="DashboardsTab"/>.
  325. /// </summary>
  326. /// <param name="header"></param>
  327. /// <returns></returns>
  328. private DynamicTabItem CreateTab(string header)
  329. {
  330. var tab = new DynamicTabItem() { Header = header };
  331. InitializeTab(tab);
  332. DashboardsTab.Items.Add(tab);
  333. return tab;
  334. }
  335. /// <summary>
  336. /// Creates a new dashboard for a tab, and then initializes the tab.
  337. /// </summary>
  338. /// <param name="tab"></param>
  339. private void InitializeNewDashboardTab(DynamicTabItem tab)
  340. {
  341. var name = CreateNewTabName("New Dashboard");
  342. _settings.Dashboards[name] = CreateForm("").SaveLayout();
  343. DashboardsTab.ChangedCommand.Execute(null);
  344. SaveSettings();
  345. tab.Header = name;
  346. InitializeTab(tab);
  347. }
  348. private void DashboardsTab_OnOnCreateTab(object sender, DynamicTabControlEventArgs args)
  349. {
  350. InitializeNewDashboardTab(args.TabItem);
  351. }
  352. private void DashboardsTab_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  353. {
  354. if (e.OriginalSource != DashboardsTab)
  355. return;
  356. if (e.AddedItems.Count == 0)
  357. return;
  358. ShutdownDashboard();
  359. if (e.AddedItems[0] is DynamicTabItem tab)
  360. {
  361. var name = (tab.Header as string)!;
  362. if (tab!.Content == null)
  363. tab.Content = CreateDashboard(name, _settings.Dashboards[name]);
  364. else
  365. {
  366. RefreshDashboard(name);
  367. }
  368. if (IsReady)
  369. {
  370. _settings.Selected = tab.TabIndex;
  371. DashboardsTab.ChangedCommand.Execute(null);
  372. }
  373. }
  374. }
  375. private void DashboardsTab_OnOnTabsChanged(object sender, EventArgs args)
  376. {
  377. SaveSettings();
  378. }
  379. #endregion
  380. #region Dashboard & Design
  381. private void SaveDashboard(string name, DynamicFormDesignGrid grid)
  382. {
  383. _settings.Dashboards[name] = grid.Form.SaveLayout();
  384. if (IsReady)
  385. SaveSettings();
  386. }
  387. private void SaveCurrentDashboard()
  388. {
  389. var name = CurrentDashboardName;
  390. if (name == null) return;
  391. var grid = CurrentDashboard;
  392. if (grid == null) return;
  393. SaveDashboard(name, grid);
  394. }
  395. private void ShutdownDashboard(string name, DynamicFormDesignGrid grid)
  396. {
  397. SaveDashboard(name, grid);
  398. foreach (var panel in _panels[grid])
  399. {
  400. panel.Shutdown();
  401. }
  402. _panels[grid].Clear();
  403. }
  404. private void ShutdownDashboard()
  405. {
  406. var name = CurrentDashboardName;
  407. if (name == null) return;
  408. var grid = CurrentDashboard;
  409. if (grid == null) return;
  410. ShutdownDashboard(name, grid);
  411. }
  412. private void RefreshDashboard(string name)
  413. {
  414. if (!_dashboards.ContainsKey(name))
  415. return;
  416. var grid = _dashboards[name];
  417. if (_panels.ContainsKey(grid))
  418. {
  419. foreach (var panel in _panels[grid])
  420. panel.Refresh();
  421. }
  422. }
  423. private FrameworkElement CreateElement<TWidget, TGroup, TProperties>(DynamicFormDesignGrid grid, DFLayoutElement<TProperties> element)
  424. where TWidget : FrameworkElement, IDashboardWidget<TGroup, TProperties>, new()
  425. where TGroup : DashboardWidgetGroup
  426. where TProperties : IDashboardProperties, new()
  427. {
  428. if (!_panels.ContainsKey(grid))
  429. _panels[grid] = new List<ICorePanel>();
  430. var dashboardName = GetDashboardElements()
  431. .Where(x => x.DashboardElement == element.GetType())
  432. .FirstOrDefault()?.WidgetCaption ?? "Unknown Dashboard";
  433. var container = DashboardContainer.Create<TWidget, TGroup, TProperties>(element, dashboardName);
  434. _panels[grid].Add(container.Panel);
  435. return container;
  436. }
  437. private FrameworkElement OnCreateElement(object sender, DynamicFormCreateElementArgs e)
  438. {
  439. var widgetType = GetVisibleDashboardElements().Where(x => x.DashboardElement == e.Element.GetType()).FirstOrDefault();
  440. if(widgetType == null)
  441. {
  442. var border = new Border
  443. {
  444. BorderBrush = new SolidColorBrush(Colors.Gray),
  445. BorderThickness = new Thickness(0.0),
  446. Margin = new Thickness(0.0),
  447. Background = ThemeManager.WorkspaceBackgroundBrush //new SolidColorBrush(Colors.Silver);
  448. };
  449. return border;
  450. }
  451. var method = typeof(UtilityDashboard)
  452. .GetMethod(nameof(CreateElement), BindingFlags.Instance | BindingFlags.NonPublic)!
  453. .MakeGenericMethod(widgetType.Widget, widgetType.Group, widgetType.Properties);
  454. return (method.Invoke(this, new object[] { sender, e.Element }) as FrameworkElement)!;
  455. }
  456. private static string GetCaption(Type groupType)
  457. {
  458. var caption = groupType.GetCustomAttribute<Caption>();
  459. if(caption != null)
  460. {
  461. return caption.Text;
  462. }
  463. return CoreUtils.Neatify(groupType.Name);
  464. }
  465. private static List<WidgetDashboardElement> GetDashboardElements()
  466. {
  467. if (_dashboardElements == null)
  468. {
  469. _dashboardElements = new();
  470. var types = CoreUtils.TypeList(
  471. AppDomain.CurrentDomain.GetAssemblies(),
  472. x => x.IsClass
  473. && !x.IsAbstract
  474. && !x.IsGenericType);
  475. foreach (var type in types)
  476. {
  477. var dashboardElementDef = type.GetSuperclassDefinition(typeof(DashboardElement<,,>));
  478. if (dashboardElementDef != null)
  479. {
  480. var dashboard = dashboardElementDef.GenericTypeArguments[0];
  481. var group = dashboardElementDef.GenericTypeArguments[1];
  482. var properties = dashboardElementDef.GenericTypeArguments[2];
  483. var requires = dashboard.GetInterfaces(typeof(IRequiresSecurity<>)).Select(x => x.GenericTypeArguments[0]);
  484. _dashboardElements.Add(new(type, dashboard, group, properties, requires.ToArray()));
  485. }
  486. }
  487. }
  488. return _dashboardElements;
  489. }
  490. private static IEnumerable<WidgetDashboardElement> GetVisibleDashboardElements()
  491. {
  492. return GetDashboardElements().Where(x =>
  493. {
  494. foreach (var require in x.SecurityTokens)
  495. {
  496. if (!Security.IsAllowed(require))
  497. return false;
  498. }
  499. return true;
  500. });
  501. }
  502. private Border CreateDashboard(string name, string layout)
  503. {
  504. var form = CreateForm(layout);
  505. var grid = new DynamicFormDesignGrid();
  506. foreach(var widget in GetVisibleDashboardElements())
  507. {
  508. grid.AddElement(widget.DashboardElement, widget.WidgetCaption, widget.GroupCaption, true);
  509. }
  510. grid.ShowBorders = false;
  511. grid.OnCreateElement += OnCreateElement;
  512. grid.OnAfterDesign += OnAfterDesign;
  513. grid.OnAfterRender += OnAfterRender;
  514. grid.Mode = FormMode.Preview;
  515. var border = new Border
  516. {
  517. BorderBrush = new SolidColorBrush(Colors.Silver),
  518. BorderThickness = new Thickness(0.75),
  519. Child = grid // scroll;
  520. };
  521. _dashboards[name] = grid;
  522. _panels[grid] = new List<ICorePanel>();
  523. grid.Form = form;
  524. grid.Initialize();
  525. return border;
  526. }
  527. private void OnAfterRender(DynamicFormDesignGrid sender)
  528. {
  529. if (!sender.IsDesigning)
  530. {
  531. if (_panels.TryGetValue(sender, out var panels))
  532. {
  533. foreach (var panel in panels)
  534. panel.Refresh();
  535. }
  536. }
  537. }
  538. private static DFLayout CreateForm(string layout)
  539. {
  540. var form = new DFLayout();
  541. if (string.IsNullOrWhiteSpace(layout))
  542. {
  543. form.ColumnWidths.Add("*");
  544. form.ColumnWidths.Add("*");
  545. form.ColumnWidths.Add("*");
  546. form.RowHeights.Add("*");
  547. form.RowHeights.Add("*");
  548. form.RowHeights.Add("*");
  549. }
  550. else
  551. {
  552. form.LoadLayout(layout);
  553. }
  554. return form;
  555. }
  556. private void OnAfterDesign(object sender)
  557. {
  558. SaveCurrentDashboard();
  559. RefreshDashboard(CurrentDashboardName);
  560. }
  561. #endregion
  562. }
  563. }