using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Comal.Classes; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using InABox.Wpf; using InABox.WPF.Themes; using Microsoft.Xaml.Behaviors.Core; using Syncfusion.Windows.Tools.Controls; using PRSDesktop.Dashboards; using InABox.Wpf.Dashboard.Editor; using InABox.Wpf.Dashboard; namespace PRSDesktop { public class DashboardFavourite : BaseObject { [TextBoxEditor] [EditorSequence(1)] public string Name { get; set; } [CheckBoxEditor] [EditorSequence(2)] public bool IsGlobal { get; set; } [NullEditor] public string Layout { get; set; } } public class CustomDashboard : BaseObject { public Guid ID { get; set; } public string Name { get; set; } public string Group { get; set; } [NullEditor] public string Layout { get; set; } } public class GlobalUtilityDashboardSettings : IGlobalConfigurationSettings { public List Favourites { get; set; } public List CustomDashboards { get; set; } public GlobalUtilityDashboardSettings() { Favourites = new(); CustomDashboards = new(); } } public class UtilityDashboardSettings : IUserConfigurationSettings { public UtilityDashboardSettings() { Dashboards = new Dictionary(); Favourites = new(); } public Dictionary Dashboards { get; set; } public List Favourites { get; set; } public int Selected { get; set; } public bool AutoHide { get; set; } } /// /// Interaction logic for UtilityDashboard.xaml /// public partial class UtilityDashboard : UserControl, IBasePanel { private readonly Dictionary _dashboards = new(); private readonly Dictionary> _panels = new(); private class WidgetDashboardElement { public Type DashboardElement { get; set; } public Type Widget { get; set; } public Type Group { get; set; } public Type Properties { get; set; } public string GroupCaption { get; set; } public string WidgetCaption { get; set; } public Type[] SecurityTokens { get; set; } public WidgetDashboardElement(Type dashboardElement, Type widget, Type group, Type properties, Type[] securityTokens) { DashboardElement = dashboardElement; Widget = widget; Group = group; Properties = properties; SecurityTokens = securityTokens; GroupCaption = UtilityDashboard.GetCaption(group); WidgetCaption = UtilityDashboard.GetCaption(widget); } } private static List? _dashboardElements; private string? CurrentDashboardName => DashboardsTab.SelectedTab?.Header?.ToString(); private DynamicFormDesignGrid? CurrentDashboard => CurrentDashboardName != null ? _dashboards.GetValueOrDefault(CurrentDashboardName) : null; private UtilityDashboardSettings _settings = new(); public UtilityDashboard() { InitializeComponent(); } public void CreateToolbarButtons(IPanelHost host) { host.CreateSetupActionIf("Manage Custom Dashboards", null, ManageCustomDashboards_Click); } private void ManageCustomDashboards_Click(PanelAction action) { var config = new GlobalConfiguration(); var settings = config.Load(); var grid = new CustomDashboardGrid(); grid.Items = settings.CustomDashboards; grid.Refresh(true, true); var dlg = new DynamicContentDialog(grid) { Title = "Manage Custom Dashboards" }; dlg.CanSave = true; if(dlg.ShowDialog() == true) { config.Save(settings); UpdateCustomDashboardList(); } } private void SaveSettings() { new UserConfiguration().Save(_settings); } #region Panel Functions & Properties public event DataModelUpdateEvent? OnUpdateDataModel; public bool IsReady { get; set; } public string SectionName => "Utility Dashboard"; public DataModel DataModel(Selection selection) { return new EmptyDataModel(); } public void Setup() { _settings = new UserConfiguration().Load(); if (_settings.Dashboards.Count == 0) _settings.Dashboards["New Dashboard"] = CreateForm("").SaveLayout(); foreach (var key in _settings.Dashboards.Keys) CreateTab(key); if (_settings.Selected >= -1 && _settings.Selected < DashboardsTab.Items.Count) DashboardsTab.SelectedIndex = _settings.Selected; //DashboardsTab.FullScreenMode = _settings.AutoHide ? FullScreenMode.ControlMode : FullScreenMode.None; } public Dictionary Selected() { return new Dictionary(); } public void Heartbeat(TimeSpan time) { } public void Refresh() { if(CurrentDashboardName is string name) { RefreshDashboard(name); } } public void Shutdown(CancelEventArgs? cancel) { foreach (var (name, grid) in _dashboards) { ShutdownDashboard(name, grid); } _panels.Clear(); } #endregion #region Favourites private IEnumerable GetFavourites() { foreach(var favourite in _settings.Favourites) { yield return favourite; } var global = new GlobalConfiguration().Load(false).Favourites; foreach (var favourite in global) { yield return favourite; } } private void ManageFavourites_Click() { var favourites = GetFavourites().ToList(); var grid = new DynamicItemsListGrid() { Items = favourites }; grid.Reconfigure(options => { options.DeleteRows = true; options.EditRows = true; options.MultiSelect = true; }); grid.OnCustomiseEditor += FavouritesGrid_OnCustomiseEditor; DynamicGridUtils.CreateGridWindow("Manage Favourites", grid).ShowDialog(); _settings.Favourites = favourites.Where(x => !x.IsGlobal).ToList(); SaveSettings(); if (Security.IsAllowed()) { var config = new GlobalConfiguration(); var global = config.Load(); global.Favourites = favourites.Where(x => x.IsGlobal).ToList(); config.Save(global); } } private void FavouritesGrid_OnCustomiseEditor(IDynamicEditorForm sender, DashboardFavourite[]? items, DynamicGridColumn column, BaseEditor editor) { if(column.ColumnName == "IsGlobal") { editor.Editable = Security.IsAllowed() ? Editable.Enabled : Editable.Disabled; } } private void SaveAsFavourite_Click(string dashboardName) { _settings.Favourites.Add(new DashboardFavourite { Name = dashboardName, Layout = _dashboards.GetValueOrDefault(dashboardName)?.Form.SaveLayout() ?? _settings.Dashboards[dashboardName] }); SaveSettings(); } private void LoadFavourite_Click(DashboardFavourite favourite) { var name = CreateNewTabName(favourite.Name); _settings.Dashboards[name] = favourite.Layout; SaveSettings(); var tab = CreateTab(name); DashboardsTab.SelectedItem = tab; } #endregion #region Tabs private void Tab_OnContextMenuOpening(object sender, DynamicTabItemContextMenuEventArgs args) { var name = (DashboardsTab.SelectedItem as DynamicTabItem)?.Header?.ToString(); if (string.IsNullOrEmpty(name)) return; DynamicFormDesignGrid grid = _dashboards[name]; var menu = args.Menu; menu.AddSeparatorIfNeeded(); var isDesigning = grid.Mode != FormMode.Preview; menu.Items.Add(new MenuItem() { Header = isDesigning ? "Close Design Mode" : "Design Mode", Command = new ActionCommand(() => { if (grid.Mode == FormMode.Designing) { grid.Mode = FormMode.Preview; SaveCurrentDashboard(); DashboardsTab.ChangedCommand.Execute(null); } else { ShutdownDashboard(); grid.Mode = FormMode.Designing; } }), Icon = new Image() { Source = (isDesigning ? InABox.Wpf.Resources.delete : PRSDesktop.Resources.pencil).AsBitmapImage(24, 24) } }); var index = 0; var favourites = GetFavourites().ToList(); if (favourites.Any()) { foreach (var favourite in favourites) { menu.AddItem(favourite.Name, null, favourite, LoadFavourite_Click, index: index++); } menu.AddSeparatorIfNeeded(index: index++); menu.AddItem("Manage Favourites", null, ManageFavourites_Click, index: index++); } menu.AddItem("Save as Favourite", null, name, SaveAsFavourite_Click, index: index++); menu.AddSeparator(index: index++); } private void Tab_OnCloseTab(object sender, DynamicTabControlEventArgs args) { var name = args.TabItem.Header?.ToString(); if (name is null) return; _dashboards.Remove(name); _settings.Dashboards.Remove(name); if (!_settings.Dashboards.Any()) { var tab = new DynamicTabItem(); InitializeNewDashboardTab(tab); DashboardsTab.Items.Add(tab); } else { DashboardsTab.ChangedCommand.Execute(null); } } private void Tab_OnTabRenamed(object sender, DynamicTabItemRenamedEventArgs args) { var oldSettings = _settings.Dashboards[args.OldName]; _settings.Dashboards.Remove(args.OldName); args.NewName = CreateNewTabName(args.NewName); if (_dashboards.TryGetValue(args.OldName, out var dashboard)) { _dashboards.Remove(args.OldName); _dashboards[args.NewName] = dashboard; _settings.Dashboards[args.NewName] = dashboard.Form.SaveLayout(); } else { _settings.Dashboards[args.NewName] = oldSettings; } } /// /// Setup events on a new tab. /// /// private void InitializeTab(DynamicTabItem tab) { tab.CanClose = true; tab.OnCloseTab += Tab_OnCloseTab; tab.CanRename = true; tab.OnTabRenamed += Tab_OnTabRenamed; tab.OnContextMenuOpening += Tab_OnContextMenuOpening; } private string CreateNewTabName(string name) { var newName = name; int i = 1; while (TabNameExists(newName)) { newName = $"{name} ({i})"; ++i; } return newName; } private bool TabNameExists(string name) { return _settings.Dashboards.ContainsKey(name); } /// /// Creates a new tab with a given header and adds it to . /// /// /// private DynamicTabItem CreateTab(string header) { var tab = new DynamicTabItem() { Header = header }; InitializeTab(tab); DashboardsTab.Items.Add(tab); return tab; } /// /// Creates a new dashboard for a tab, and then initializes the tab. /// /// private void InitializeNewDashboardTab(DynamicTabItem tab) { var name = CreateNewTabName("New Dashboard"); _settings.Dashboards[name] = CreateForm("").SaveLayout(); DashboardsTab.ChangedCommand.Execute(null); SaveSettings(); tab.Header = name; InitializeTab(tab); } private void DashboardsTab_OnOnCreateTab(object sender, DynamicTabControlEventArgs args) { InitializeNewDashboardTab(args.TabItem); } private void DashboardsTab_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.OriginalSource != DashboardsTab) return; if (e.AddedItems.Count == 0) return; ShutdownDashboard(); if (e.AddedItems[0] is DynamicTabItem tab) { var name = (tab.Header as string)!; if (tab!.Content == null) tab.Content = CreateDashboard(name, _settings.Dashboards[name]); else { RefreshDashboard(name); } if (IsReady) { _settings.Selected = tab.TabIndex; DashboardsTab.ChangedCommand.Execute(null); } } } private void DashboardsTab_OnOnTabsChanged(object sender, EventArgs args) { SaveSettings(); } #endregion #region Dashboard & Design private void SaveDashboard(string name, DynamicFormDesignGrid grid) { _settings.Dashboards[name] = grid.Form.SaveLayout(); if (IsReady) SaveSettings(); } private void SaveCurrentDashboard() { var name = CurrentDashboardName; if (name == null) return; var grid = CurrentDashboard; if (grid == null) return; SaveDashboard(name, grid); } private void ShutdownDashboard(string name, DynamicFormDesignGrid grid) { SaveDashboard(name, grid); var cancel = new CancelEventArgs(); foreach (var panel in _panels[grid]) { panel.Shutdown(cancel); if (cancel.Cancel) { return; } } _panels[grid].Clear(); } private void ShutdownDashboard() { var name = CurrentDashboardName; if (name == null) return; var grid = CurrentDashboard; if (grid == null) return; ShutdownDashboard(name, grid); } private void RefreshDashboard(string name) { if (!_dashboards.ContainsKey(name)) return; var grid = _dashboards[name]; if (_panels.ContainsKey(grid)) { foreach (var panel in _panels[grid]) panel.Refresh(); } } private FrameworkElement CreateElement(DynamicFormDesignGrid grid, DFLayoutElement element) where TWidget : FrameworkElement, IDashboardWidget, new() where TProperties : IConfigurationSettings, IDashboardProperties, new() { if (!_panels.ContainsKey(grid)) _panels[grid] = new List(); string dashboardName; if(element is CustomDashboardElement custom) { var customDashboard = new GlobalConfiguration().Load() .CustomDashboards.FirstOrDefault(x => x.ID == custom.Properties.DashboardID); dashboardName = customDashboard?.Name ?? "Unknown Dashboard"; } else { dashboardName = GetDashboardElements() .Where(x => x.DashboardElement == element.GetType()) .FirstOrDefault()?.WidgetCaption ?? "Unknown Dashboard"; } var container = DashboardContainer.Create(element, dashboardName); _panels[grid].Add(container.Panel); return container; } private FrameworkElement OnCreateElement(object sender, DynamicFormCreateElementArgs e) { var method = typeof(UtilityDashboard).GetMethod(nameof(CreateElement), BindingFlags.Instance | BindingFlags.NonPublic)!; if(e.Element is CustomDashboardElement custom) { method = method.MakeGenericMethod(typeof(CustomDashboardWidget), typeof(CustomDashboardProperties)); } else { var widgetType = GetVisibleDashboardElements().Where(x => x.DashboardElement == e.Element.GetType()).FirstOrDefault(); if(widgetType == null) { var border = new Border { BorderBrush = new SolidColorBrush(Colors.Gray), BorderThickness = new Thickness(0.0), Margin = new Thickness(0.0), Background = ThemeManager.WorkspaceBackgroundBrush //new SolidColorBrush(Colors.Silver); }; return border; } method = method.MakeGenericMethod(widgetType.Widget, widgetType.Properties); } return (method.Invoke(this, new object[] { sender, e.Element }) as FrameworkElement)!; } private static string GetCaption(Type groupType) { var caption = groupType.GetCustomAttribute(); if(caption != null) { return caption.Text; } return CoreUtils.Neatify(groupType.Name); } private static List GetDashboardElements() { if (_dashboardElements == null) { _dashboardElements = new(); var types = CoreUtils.Entities.Where(x => x.IsClass && !x.IsGenericType && x.GetInterfaces().Contains(typeof(IDashboardElement))); foreach (var type in types) { var dashboardElementDef = type.GetSuperclassDefinition(typeof(DashboardElement<,,>)); if (dashboardElementDef != null) { var dashboard = dashboardElementDef.GenericTypeArguments[0]; var group = dashboardElementDef.GenericTypeArguments[1]; var properties = dashboardElementDef.GenericTypeArguments[2]; var requires = dashboard.GetInterfaces(typeof(IRequiresSecurity<>)).Select(x => x.GenericTypeArguments[0]); _dashboardElements.Add(new(type, dashboard, group, properties, requires.ToArray())); } } } return _dashboardElements; } private static IEnumerable GetVisibleDashboardElements() { return GetDashboardElements().Where(x => { foreach (var require in x.SecurityTokens) { if (!Security.IsAllowed(require)) return false; } return true; }); } private void UpdateCustomDashboardList() { var customDashboards = new GlobalConfiguration().Load().CustomDashboards; foreach(var grid in _dashboards.Values) { grid.ClearElementTypesAndActions(); AddCustomDashboardsToGrid(grid, customDashboards); } } private void AddCustomDashboardsToGrid(DynamicFormDesignGrid grid, List customDashboards) { foreach(var widget in GetVisibleDashboardElements()) { grid.AddElementType(widget.DashboardElement, widget.WidgetCaption, widget.GroupCaption, true); } foreach(var customDashboard in customDashboards) { grid.AddElementType(typeof(CustomDashboardElement), customDashboard.Name, customDashboard.Group.NotWhiteSpaceOr("Custom"), customDashboard, AddCustom_Click, allowduplicate: true); } grid.AddElementAction("Create New", InABox.Wpf.Resources.add, "", null, CreateNewCustom_Click); } private Border CreateDashboard(string name, string layout) { var form = CreateForm(layout); var grid = new DynamicFormDesignGrid(); var customDashboards = new GlobalConfiguration().Load().CustomDashboards; AddCustomDashboardsToGrid(grid, customDashboards); grid.ShowBorders = false; grid.OnCreateElement += OnCreateElement; grid.OnAfterDesign += OnAfterDesign; grid.OnAfterRender += OnAfterRender; grid.CustomiseElementContextMenu += Grid_CustomiseElementContextMenu; grid.Mode = FormMode.Preview; var border = new Border { BorderBrush = new SolidColorBrush(Colors.Silver), BorderThickness = new Thickness(0.75), Child = grid // scroll; }; _dashboards[name] = grid; _panels[grid] = new List(); grid.Form = form; grid.Initialize(); return border; } private void Grid_CustomiseElementContextMenu(ContextMenu menu, DFLayoutElement element) { if(element is CustomDashboardElement dashboardElement) { menu.AddItem("Edit Dashboard", null, dashboardElement, EditDashboard_Click); } } private void EditDashboard_Click(CustomDashboardElement element) { var config = new GlobalConfiguration(); var settings = config.Load(); var customDashboard = settings.CustomDashboards.FirstOrDefault(x => x.ID == element.Properties.DashboardID); if(customDashboard is null) { return; } var dashboard = DynamicDashboardUtils.Deserialize(customDashboard.Layout); if(dashboard is null) { return; } var editor = new DynamicDashboardEditor(dashboard); editor.DashboardName = customDashboard.Name; editor.DashboardGroup = customDashboard.Group; var dlg = new DynamicContentDialog(editor) { Title = "Edit dashboard", SizeToContent = SizeToContent.Height, CanSave = true }; if(dlg.ShowDialog() == true) { customDashboard.Layout = DynamicDashboardUtils.Serialize(editor.GetDashboard()); customDashboard.Name = editor.DashboardName; customDashboard.Group = editor.DashboardGroup; config.Save(settings); UpdateCustomDashboardList(); } } private DFLayoutElement AddCustom_Click(CustomDashboard dashboard) { var element = new CustomDashboardElement(); element.Properties = new CustomDashboardProperties { DashboardID = dashboard.ID }; return element; } private DFLayoutElement? CreateNewCustom_Click(object? tag) { var dashboard = new DynamicDashboard(); var editor = new DynamicDashboardEditor(dashboard); editor.DashboardName = "New Dashboard"; editor.DashboardGroup = "Custom"; var dlg = new DynamicContentDialog(editor) { Title = "Create new dashboard", SizeToContent = SizeToContent.Height, CanSave = true }; if(dlg.ShowDialog() == true) { var config = new GlobalConfiguration(); var settings = config.Load(); var newDashboard = new CustomDashboard { Layout = DynamicDashboardUtils.Serialize(editor.GetDashboard()), Name = editor.DashboardName, Group = editor.DashboardGroup, ID = Guid.NewGuid() }; settings.CustomDashboards.Add(newDashboard); config.Save(settings); var element = new CustomDashboardElement(); element.Properties = new CustomDashboardProperties { DashboardID = newDashboard.ID }; UpdateCustomDashboardList(); return element; } else { return null; } } private void OnAfterRender(DynamicFormDesignGrid sender) { if (!sender.IsDesigning) { if (_panels.TryGetValue(sender, out var panels)) { foreach (var panel in panels) panel.Refresh(); } } } private static DFLayout CreateForm(string layout) { var form = new DFLayout(); if (string.IsNullOrWhiteSpace(layout)) { form.ColumnWidths.Add("*"); form.ColumnWidths.Add("*"); form.ColumnWidths.Add("*"); form.RowHeights.Add("*"); form.RowHeights.Add("*"); form.RowHeights.Add("*"); } else { form.LoadLayout(layout); } return form; } private void OnAfterDesign(object sender) { SaveCurrentDashboard(); if(CurrentDashboardName is string name) // Null-check { RefreshDashboard(name); } } #endregion } }