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