using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Reflection; using System.Windows; using System.Windows.Controls; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; namespace InABox.Wpf; public interface IPanelActionItem { } public interface IPanelActionEntry { object? Data { get; set; } string? Caption { get; set; } Bitmap? Image { get; set; } void Execute(); } public class PanelActionEntry : DependencyObject, IPanelActionEntry { public static readonly DependencyProperty DataProperty = DependencyProperty.Register( nameof(Data), typeof(T), typeof(PanelActionEntry) ); public T? Data { get; set; } object? IPanelActionEntry.Data { get => this.Data; set => this.Data = value == null ? default(T) : (T)value; } public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register( nameof(Caption), typeof(string), typeof(PanelActionEntry), new PropertyMetadata("") ); public string? Caption { get; set; } public static readonly DependencyProperty ImageProperty = DependencyProperty.Register( nameof(Image), typeof(Bitmap), typeof(PanelActionEntry) ); public Bitmap? Image { get => GetValue(ImageProperty) as Bitmap; set => SetValue(ImageProperty, value); } public Action>? OnExecute { get; set; } public PanelActionEntry() { } public PanelActionEntry(string? caption, Bitmap? image, T? data, Action>? onExecute) { Data = data; Caption = caption; Image = image; OnExecute = onExecute; } public void Execute() { OnExecute?.Invoke(this); } } public class PanelAction : DependencyObject, IPanelActionItem { public Func? OnPopulate { get; set; } public Action? OnExecute { get; set; } public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register( nameof(IsEnabled), typeof(bool), typeof(PanelAction), new PropertyMetadata(true) ); public bool IsEnabled { get => (bool)GetValue(IsEnabledProperty); set => SetValue(IsEnabledProperty, value); } public static readonly DependencyProperty VisibilityProperty = DependencyProperty.Register( nameof(Visibility), typeof(Visibility), typeof(PanelAction), new PropertyMetadata(Visibility.Visible) ); public Visibility Visibility { get => (Visibility)GetValue(VisibilityProperty); set => SetValue(VisibilityProperty, value); } public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register( nameof(Caption), typeof(string), typeof(PanelAction), new PropertyMetadata("") ); public string Caption { get => (string)GetValue(CaptionProperty); set => SetValue(CaptionProperty, value); } public static readonly DependencyProperty ImageProperty = DependencyProperty.Register( nameof(Image), typeof(Bitmap), typeof(PanelAction) ); public Bitmap? Image { get => GetValue(ImageProperty) as Bitmap; set => SetValue(ImageProperty, value); } public static readonly DependencyProperty MenuProperty = DependencyProperty.Register( nameof(Menu), typeof(ContextMenu), typeof(PanelAction) ); public ContextMenu? Menu { get => GetValue(MenuProperty) as ContextMenu; set => SetValue(MenuProperty, value); } public PanelAction() { } public PanelAction(string caption, Bitmap? image, Action? onExecute, Func? onPopulate = null) { Caption = caption; Image = image; OnPopulate = onPopulate; OnExecute = onExecute; } public void Execute() { OnExecute?.Invoke(this); } public IPanelActionEntry[]? Populate() { return OnPopulate?.Invoke(this); } } public class PanelActionSeparator : IPanelActionItem { } public interface ICorePanel { void Setup(); /// /// Shutdown the panel. /// /// If the operation can be cancelled, this is not . void Shutdown(CancelEventArgs? cancel); void Refresh(); } public interface IBasePanel : ICorePanel, IDataModelSource { bool IsReady { get; set; } void CreateToolbarButtons(IPanelHost host); Dictionary Selected(); void Heartbeat(TimeSpan time); } public interface IPanel : IBasePanel { } public interface IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { public TProperties Properties { get; set; } } public interface IPropertiesPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() where TSecurity : ISecurityDescriptor, new() { } public static class PanelUtils { #region Properties public static void InitializePanelProperties(IBasePanel panel) { var propertiesInterface = panel.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<>)); if (propertiesInterface is not null) { var propertiesType = propertiesInterface.GenericTypeArguments[0]; var method = typeof(PanelUtils) .GetMethod(nameof(InitializePanelPropertiesGeneric), BindingFlags.Public | BindingFlags.Static) ?.MakeGenericMethod(panel.GetType(), propertiesType) .Invoke(null, new object?[] { panel }); } } public static void InitializePanelPropertiesGeneric(TPanel panel) where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { panel.Properties = LoadPanelProperties(); } public static TProperties LoadPanelProperties() where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { var config = new GlobalConfiguration(); return config.Load(); } public static void SavePanelProperties(TProperties properties) where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { var config = new GlobalConfiguration(); config.Save(properties); } public static void EditPanelProperties() where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { var properties = LoadPanelProperties(); bool result; if (DynamicGridUtils.TryFindDynamicGrid(typeof(DynamicGrid<>), typeof(TProperties), out var gridType)) { var grid = (Activator.CreateInstance(gridType) as DynamicGrid)!; result = grid.EditItems(new TProperties[] { properties }); } else { var grid = new DynamicItemsListGrid(); result = grid.EditItems(new TProperties[] { properties }); } if (result) { SavePanelProperties(properties); } } public static void ConfigurePanel(IBasePanel panel) { var propertiesInterface = panel.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<>))!; var propertiesType = propertiesInterface.GenericTypeArguments[0]; var basemethod = typeof(PanelUtils) .GetMethod(nameof(EditPanelProperties), BindingFlags.Public | BindingFlags.Static); if (basemethod == null) return; var method = basemethod?.MakeGenericMethod(panel.GetType(), propertiesType); if (method != null) method.Invoke(null, Array.Empty()); } #endregion public static T LoadPanel() where T : class, IBasePanel, new() { var panel = new T(); InitializePanelProperties(panel); panel.IsReady = false; panel.Setup(); panel.IsReady = true; return panel; } public static void UnloadPanel(IBasePanel panel, CancelEventArgs? cancel) { try { if(panel is ISubPanelHost host) { host.ShutdownSubPanels(cancel); } panel.Shutdown(cancel); if (cancel?.Cancel == true) { return; } } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Error in UnloadPanel(): {0}\n{1}", e.Message, e.StackTrace)); } } } public interface IPanelHost { void CreatePanelAction(PanelAction action); void CreateReport(PanelAction action); void CreateSetupAction(PanelAction action); void CreateSetupSeparator(); } public static class IPanelHostExtensions { public static void CreateSetupAction(this IPanelHost host, string caption, Bitmap? image, Action onExecute, ContextMenu? menu = null) { host.CreateSetupAction(new PanelAction(caption, image, onExecute) { Menu = menu }); } public static void CreateSetupActionIf(this IPanelHost host, string caption, Bitmap? image, Action onExecute, bool canView, ContextMenu? menu = null) { if (canView) { host.CreateSetupAction(new PanelAction(caption, image, onExecute) { Menu = menu }); } } public static void CreateSetupActionIf(this IPanelHost host, string caption, Bitmap? image, Action onExecute, ContextMenu? menu = null) where TSecurity : ISecurityDescriptor, new() { host.CreateSetupActionIf(caption, image, onExecute, Security.IsAllowed(), menu); } public static void CreateSetupActionIfCanView(this IPanelHost host, string caption, Bitmap? image, Action onExecute, ContextMenu? menu = null) where T : Entity, new() { host.CreateSetupActionIf(caption, image, onExecute, Security.CanView(), menu); } } /// /// Implement this interface to cause a class to act as a host of . Then, you can use the extension functions /// and /// to interact with it. /// /// /// If you mark an as an , then the shutdown method is called automatically by . /// public interface ISubPanelHost { List SubPanels { get; } static ISubPanelHost Global = new GlobalSubPanelHost(); private class GlobalSubPanelHost : ISubPanelHost { public List SubPanels { get; set; } = new(); } } public static class ISubPanelHostExtensions { public static void ShutdownSubPanels(this ISubPanelHost host, CancelEventArgs? cancel) { ISubPanel[] panels; lock (host.SubPanels) { panels = host.SubPanels.ToArray(); host.SubPanels.Clear(); var isCancelled = false; foreach(var panel in panels) { if (isCancelled) { host.SubPanels.Add(panel); } else { panel.Shutdown(cancel); if (cancel?.Cancel == true) { isCancelled = true; host.SubPanels.Add(panel); } } } } } public static void AddSubPanel(this ISubPanelHost host, ISubPanel panel) { host.SubPanels.Add(panel); panel.SubPanelClosed += p => { lock (host.SubPanels) { host.SubPanels.Remove(p); } }; } } /// /// An is a non-modal window, which is tied to a parent /// public interface ISubPanel { public delegate void ClosedEvent(ISubPanel panel); /// /// Event to be called when a sub-panel closes itself; in this case, will not be called. This allows /// the host to get rid of the sub-panel, instead of keeping it forever. /// /// /// You may call this after has been called. /// public event ClosedEvent? SubPanelClosed; /// /// Shutdown the panel. /// /// If the operation can be cancelled, this is not void Shutdown(CancelEventArgs? cancel); }