UtilityDashboard.xaml.cs 20 KB

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