| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 | 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<T> : DependencyObject, IPanelActionEntry{        public static readonly DependencyProperty DataProperty = DependencyProperty.Register(        nameof(Data),        typeof(T),        typeof(PanelActionEntry<T>)    );    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<T>),        new PropertyMetadata("")    );    public string? Caption { get; set; }        public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(        nameof(Image),        typeof(Bitmap),        typeof(PanelActionEntry<T>)    );        public Bitmap? Image    {         get => GetValue(ImageProperty) as Bitmap;        set => SetValue(ImageProperty, value);    }        public Action<PanelActionEntry<T>>? OnExecute { get; set; }    public PanelActionEntry()    {            }        public PanelActionEntry(string? caption, Bitmap? image, T? data, Action<PanelActionEntry<T>>? onExecute)    {        Data = data;        Caption = caption;        Image = image;        OnExecute = onExecute;    }    public void Execute()    {        OnExecute?.Invoke(this);    }    }public class PanelAction : DependencyObject, IPanelActionItem{    public Func<PanelAction,IPanelActionEntry[]>? OnPopulate { get; set; }    public Action<PanelAction>? 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<PanelAction>? onExecute,        Func<PanelAction, IPanelActionEntry[]>? 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();    /// <summary>    /// Shutdown the panel.    /// </summary>    /// <param name="cancel">If the operation can be cancelled, this is not <see langword="null"/>.</param>    void Shutdown(CancelEventArgs? cancel);    void Refresh();}public interface IBasePanel : ICorePanel, IDataModelSource{    bool IsReady { get; set; }    void CreateToolbarButtons(IPanelHost host);    Dictionary<string, object[]> Selected();    void Heartbeat(TimeSpan time);}public interface IPanel<T> : IBasePanel{}public interface IPropertiesPanel<TProperties>    where TProperties : BaseObject, IGlobalConfigurationSettings, new(){    public TProperties Properties { get; set; }}public interface IPropertiesPanel<TProperties, TSecurity> : IPropertiesPanel<TProperties>    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, TProperties>(TPanel panel)        where TPanel : IPropertiesPanel<TProperties>        where TProperties : BaseObject, IGlobalConfigurationSettings, new()    {        panel.Properties = LoadPanelProperties<TPanel, TProperties>();    }    public static TProperties LoadPanelProperties<TPanel, TProperties>()        where TPanel : IPropertiesPanel<TProperties>        where TProperties : BaseObject, IGlobalConfigurationSettings, new()    {        var config = new GlobalConfiguration<TProperties>();        return config.Load();    }    public static void SavePanelProperties<TPanel, TProperties>(TProperties properties)        where TPanel : IPropertiesPanel<TProperties>        where TProperties : BaseObject, IGlobalConfigurationSettings, new()    {        var config = new GlobalConfiguration<TProperties>();        config.Save(properties);    }    public static void EditPanelProperties<TPanel, TProperties>()        where TPanel : IPropertiesPanel<TProperties>        where TProperties : BaseObject, IGlobalConfigurationSettings, new()    {        var properties = LoadPanelProperties<TPanel, TProperties>();        bool result;        if (DynamicGridUtils.TryFindDynamicGrid(typeof(DynamicGrid<>), typeof(TProperties), out var gridType))        {            var grid = (Activator.CreateInstance(gridType) as DynamicGrid<TProperties>)!;            result = grid.EditItems(new TProperties[] { properties });        }        else        {            var grid = new DynamicItemsListGrid<TProperties>();            result = grid.EditItems(new TProperties[] { properties });        }        if (result)        {            SavePanelProperties<TPanel, TProperties>(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<object?>());    }    #endregion    public static T LoadPanel<T>() 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<PanelAction> 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<PanelAction> onExecute, bool canView, ContextMenu? menu = null)    {        if (canView)        {            host.CreateSetupAction(new PanelAction(caption, image, onExecute) { Menu = menu });        }    }    public static void CreateSetupActionIf<TSecurity>(this IPanelHost host, string caption, Bitmap? image, Action<PanelAction> onExecute, ContextMenu? menu = null)        where TSecurity : ISecurityDescriptor, new()    {        host.CreateSetupActionIf(caption, image, onExecute, Security.IsAllowed<TSecurity>(), menu);    }    public static void CreateSetupActionIfCanView<T>(this IPanelHost host, string caption, Bitmap? image, Action<PanelAction> onExecute, ContextMenu? menu = null)        where T : Entity, new()    {        host.CreateSetupActionIf(caption, image, onExecute, Security.CanView<T>(), menu);    }}/// <summary>/// Implement this interface to cause a class to act as a host of <see cref="ISubPanel"/>. Then, you can use the extension functions/// <see cref="ISubPanelHostExtensions.ShutdownSubPanels(ISubPanelHost, CancelEventArgs?)"/> and/// <see cref="ISubPanelHostExtensions.AddSubPanel(ISubPanelHost, ISubPanel)"/> to interact with it./// </summary>/// <remarks>/// If you mark an <see cref="IPanel{T}"/> as an <see cref="ISubPanelHost"/>, then the shutdown method is called automatically by <see cref="IPanelHost"/>./// </remarks>public interface ISubPanelHost{    List<ISubPanel> SubPanels { get; }    static ISubPanelHost Global = new GlobalSubPanelHost();    private class GlobalSubPanelHost : ISubPanelHost    {        public List<ISubPanel> 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);            }        };    }}/// <summary>/// An <see cref="ISubPanel"/> is a non-modal window, which is tied to a parent /// </summary>public interface ISubPanel{    public delegate void ClosedEvent(ISubPanel panel);    /// <summary>    /// Event to be called when a sub-panel closes itself; in this case, <see cref="Shutdown(CancelEventArgs?)"/> will not be called. This allows    /// the host to get rid of the sub-panel, instead of keeping it forever.    /// </summary>    /// <remarks>    /// You may call this after <see cref="Shutdown(CancelEventArgs?)"/> has been called.    /// </remarks>    public event ClosedEvent? SubPanelClosed;    /// <summary>    /// Shutdown the panel.    /// </summary>    /// <param name="cancel">If the operation can be cancelled, this is not <see langword="null"/></param>    void Shutdown(CancelEventArgs? cancel);}
 |