| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 | using System;using System.Collections;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Reflection;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using InABox.Clients;using InABox.Core;using InABox.Wpf;using InABox.Reports.Common;using Syncfusion.Data.Extensions;namespace InABox.DynamicGrid{    [Caption("Set Default Column Selections")]    public class CanSetDefaultColumns : EnabledSecurityDescriptor<CoreLicense>    {    }    [LibraryInitializer]    public static class DynamicGridUtils    {        private static IEnumerable<Type>? _allm2mtypes;        private static IEnumerable<Type>? _allm2mpages;        private static IEnumerable<Type>? _allo2mtypes;        private static IEnumerable<Type>? _allo2mpages;        private static IEnumerable<Type>? _allcepages;        private static IEnumerable<Type>? _alleltypes;                private static Dictionary<Type, IEnumerable<IDynamicEditorPage>> _onetomanypages = new Dictionary<Type, IEnumerable<IDynamicEditorPage>>();        private static Dictionary<Type, IEnumerable<IDynamicEditorPage>> _manytomanytomanypages = new Dictionary<Type, IEnumerable<IDynamicEditorPage>>();        private static Dictionary<Type, IEnumerable<IDynamicEditorPage>> _enclosedlistpages = new Dictionary<Type, IEnumerable<IDynamicEditorPage>>();        private static Dictionary<Type, IEnumerable<IDynamicEditorPage>> _customeditorpages = new Dictionary<Type, IEnumerable<IDynamicEditorPage>>();        // HACK: These are really dumb        public static Action<ReportTemplate, DataModel>? PreviewReport { get; set; }        public static Action<FrameworkElement?, string, DataModel, bool>? PrintMenu { get; set; }        public static readonly MainResources Resources = new();        public static void RegisterClasses()        {            // String assyname = "_" + Assembly.GetExecutingAssembly().GetName().Name;            // AssemblyName assemblyName = new AssemblyName(assyname);            // AppDomain appDomain = Thread.GetDomain();            //            // String assyFile = String.Format("{0}.dll", assemblyName.Name);            // String path = "";            // if (Assembly.GetEntryAssembly() != null)            // {            //     path = Path.Combine(            //         Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),            //         Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location)            //     );            // }            // else            // {            //     path = Path.Combine(            //         Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),            //         Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location)            //     );            // }            //            // if (!Directory.Exists(path))            //     Directory.CreateDirectory(path);            // AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, path);            //            // ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assyFile); //,true);            //            // if (_allm2mtypes == null)            // {            //     _allm2mtypes = CoreUtils.TypeList(            //         AppDomain.CurrentDomain.GetAssemblies(),            //         x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>))            //     );            // }            //            // var maps = _allm2mtypes.Where(x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Last().Equals(typeof(Document))));            //            // foreach (var map in maps)            // {            //     var intf = map.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Last().Equals(typeof(Document)));            //     Type entity = intf.GenericTypeArguments.First();            //     Type basetype = typeof(DynamicDocumentGrid<,>).MakeGenericType(map, entity);            //     TypeBuilder tbService = module.DefineType(String.Format("{0}", map.EntityName().Replace(".", "_")), TypeAttributes.Public | TypeAttributes.Class);            //     tbService.SetParent(basetype);            //     Type final = tbService.CreateType();            // }            //            // try            // {            //     assemblyBuilder.Save(assyFile);            // }            // catch (Exception e)            // {            //     Logger.Send(LogType.Error, "", String.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));            // }                    }        #region Pages        public static IEnumerable<Type> GetManyToManyTypes(Type type)        {            _allm2mtypes ??= CoreUtils.TypeList(                AppDomain.CurrentDomain.GetAssemblies(),                x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>))            );            return _allm2mtypes.Where(x => x.GetInterfaces().Any(                i => i.IsGenericType                && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>)                && i.GenericTypeArguments[0] == type)            );        }        public static void LoadManyToManyPages(Type type, DynamicEditorPages pages)        {            if (!_manytomanytomanypages.ContainsKey(type))            {                var cache = new List<IDynamicEditorPage>();                var maps = GetManyToManyTypes(type);                foreach (var map in maps)                {                    if (ClientFactory.IsSupported(map))                    {                        _allm2mpages ??= CoreUtils.TypeList(                                AppDomain.CurrentDomain.GetAssemblies(),                                x => x.IsClass                                     && !x.IsAbstract                                     && !x.IsGenericType                                     && x.GetInterfaces().Any(                                         i => i.IsGenericType                                              && i.GetGenericTypeDefinition() == typeof(IDynamicManyToManyGrid<,>)                                     )                            );                        var subtypes = _allm2mpages.Where(                            x => x.GetInterfaces().Any(i =>                                i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicManyToManyGrid<,>) &&                                i.GenericTypeArguments.First().Equals(map) && i.GenericTypeArguments.Last().Equals(type))                        );                        IDynamicEditorPage page;                        if (subtypes.Any())                        {                            page = (Activator.CreateInstance(subtypes.First()) as IDynamicEditorPage)!;                        }                        else                        {                            var defType = typeof(DynamicManyToManyGrid<,>).MakeGenericType(map, type);                            page = (Activator.CreateInstance(defType) as IDynamicEditorPage)!;                        }                        cache.Add(page);                    }                }                _manytomanytomanypages[type] = cache;            }            pages.AddRange(_manytomanytomanypages[type]);        }        public static IEnumerable<Type> GetOneToManyTypes(Type type)        {            _allo2mtypes ??= CoreUtils.TypeList(                AppDomain.CurrentDomain.GetAssemblies(),                x => x.GetInterfaces().Any(i =>                    i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IOneToMany<>) && x.GetCustomAttribute<ObsoleteAttribute>() == null)            );            return _allo2mtypes                .Where(x => x.GetInterfaces().Any(i =>                    i.IsGenericType                    && i.GetGenericTypeDefinition() == typeof(IOneToMany<>)                    && i.GenericTypeArguments.Contains(type)))                .OrderBy(x => x.EntityName());        }                public static void LoadOneToManyPages(Type type, DynamicEditorPages pages)        {            if (!_onetomanypages.ContainsKey(type))            {                var cache = new List<IDynamicEditorPage>();                var maps = GetOneToManyTypes(type);                foreach (var map in maps)                {                    if (ClientFactory.IsSupported(map))                    {                        _allo2mpages ??= CoreUtils.TypeList(                                AppDomain.CurrentDomain.GetAssemblies(),                                x =>                                    x.IsClass                                    && !x.IsAbstract                                    && !x.IsGenericType                                    && x.GetInterfaces().Any(i =>                                        i.IsGenericType                                        && i.GetGenericTypeDefinition() == typeof(IDynamicOneToManyGrid<,>)                                    )                            );                        var subtypes = _allo2mpages.Where(x => x.GetInterfaces().Any(                                i => i.IsGenericType                                     && i.GetGenericTypeDefinition() == typeof(IDynamicOneToManyGrid<,>)                                     && i.GenericTypeArguments.First().Equals(type)                                     && i.GenericTypeArguments.Last().Equals(map)                            )                        );                        IDynamicEditorPage page;                        if (subtypes.Any())                        {                            page = (Activator.CreateInstance(subtypes.First()) as IDynamicEditorPage)!;                        }                        else                        {                            var defType = typeof(DynamicOneToManyGrid<,>).MakeGenericType(type, map);                            page = (Activator.CreateInstance(defType) as IDynamicEditorPage)!;                        }                        cache.Add(page);                    }                }                _onetomanypages[type] = cache;            }            pages.AddRange(_onetomanypages[type]);        }        public static void LoadCustomEditorPages(Type type, DynamicEditorPages pages)        {            if (!_customeditorpages.ContainsKey(type))            {                var cache = new List<IDynamicEditorPage>();                _allcepages ??= CoreUtils.TypeList(                        AppDomain.CurrentDomain.GetAssemblies(),                        x => x.IsClass                             && !x.IsAbstract                             && !x.IsGenericType                             && x.GetInterfaces().Any(i =>                                 i.IsGenericType                                 && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>)                             )                    );                var pagetypes = _allcepages.Where(x => x.GetInterfaces().Any(i =>                    i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>) &&                    i.GenericTypeArguments.First().Equals(type)));                foreach (var pagetype in pagetypes)                {                    var page = (Activator.CreateInstance(pagetype) as IDynamicEditorPage)!;                    cache.Add(page);                }                _customeditorpages[type] = cache;            }            pages.AddRange(_customeditorpages[type]);        }        public static void LoadEnclosedListPages(Type type, DynamicEditorPages pages)        {            if (!_enclosedlistpages.ContainsKey(type))            {                var cache = new List<IDynamicEditorPage>();                foreach (var property in type.GetProperties())                {                    if (property.PropertyType.GetInterfaces().Contains(typeof(IList)))                    {                        var curtype = property.PropertyType;                        var gentype = property.PropertyType.GetGenericArguments().FirstOrDefault();                        while (gentype == null && curtype?.BaseType != null)                        {                            curtype = curtype.BaseType;                            gentype = curtype?.GetGenericArguments().FirstOrDefault();                        }                        if (gentype != null)                            if (gentype.IsSubclassOf(typeof(BaseObject)))                            {                                var editor = property.GetCustomAttributes().FirstOrDefault(x => x is BaseEditor);                                if (editor == null || !(editor is NullEditor))                                {                                    _alleltypes ??= CoreUtils.TypeList(                                            AppDomain.CurrentDomain.GetAssemblies(),                                            x => x.IsClass                                                 && !x.IsAbstract                                                 && !x.IsGenericType                                                 && x.GetInterfaces().Any(i =>                                                     i.IsGenericType                                                     && i.GetGenericTypeDefinition() == typeof(IDynamicEnclosedListGrid<,>)                                                 )                                        );                                    var subtypes = _alleltypes.Where(                                        x => x.GetInterfaces().Any(                                            i => i.IsGenericType                                                 && i.GetGenericTypeDefinition() == typeof(IDynamicEnclosedListGrid<,>)                                                 && i.GenericTypeArguments.First().Equals(type)                                                 && i.GenericTypeArguments.Last().Equals(gentype)                                        )                                    );                                    IDynamicEditorPage page;                                    if (subtypes.Any())                                    {                                        page = (Activator.CreateInstance(subtypes.First(), property) as IDynamicEditorPage)!;                                        cache.Add(page);                                    }                                    else                                    {                                        subtypes = _alleltypes.Where(x => x.GetInterfaces().Any(i => i.GenericTypeArguments.Last().Equals(gentype)));                                        if (subtypes.Any())                                        {                                            var defType = subtypes.First().MakeGenericType(type);                                            page = (Activator.CreateInstance(defType, property) as IDynamicEditorPage)!;                                            cache.Add(page);                                        }                                        else                                        {                                            try                                            {                                                var defType = typeof(DynamicEnclosedListGrid<,>).MakeGenericType(type, gentype);                                                page = (Activator.CreateInstance(defType, property) as IDynamicEditorPage)!;                                                cache.Add(page);                                            }                                            catch (Exception e)                                            {                                                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));                                            }                                        }                                    }                                }                            }                    }                }                _enclosedlistpages[type] = cache;            }            pages.AddRange(_enclosedlistpages[type]);        }        #endregion        #region Editor Values        public static Dictionary<string, object?> GetValues(BaseObject item, string name, IEnumerable<IProperty>? props = null)        {            var result = new Dictionary<string, object?>();            props ??= DatabaseSchema.Properties(item.GetType()).Where(x => x.Editor is not NullEditor);            foreach (var prop in props)                if (prop is CustomProperty)                    try                    {                        result[prop.Name] = item.UserProperties[prop.Name];                    }                    catch (Exception e)                    {                        Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));                    }                else                    try                    {                        object? value;                        var getter = prop.Getter();                        if (getter != null)                            value = getter.Invoke(item);                        else                            value = CoreUtils.GetPropertyValue(item, prop.Name);                        result[prop.Name] = value;                    }                    catch (Exception e)                    {                        Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));                    }            return result;        }        public static Dictionary<string, object?> UpdateEditorValue(BaseObject[] items, string name, object value)        {            Logger.Send(LogType.Information, "", string.Format("DynamicGridUtils.UpdateEditorValue({0},{1},{2})", items.Length, name, value));            var sw = new Stopwatch();            var changes = new Dictionary<string, object?>();            var props = DatabaseSchema.Properties(items.First().GetType()).Where(x => x.Editor is not NullEditor);            foreach (var item in items)            {                //Dictionary<String, object> previous = new Dictionary<string, object>();                var previous = GetValues(item, name, props);                //if (item.OriginalValues != null)                //{                //    foreach (var key in item.OriginalValues.Keys)                //        previous[key] = item.OriginalValues[key];                //}                var prop = DatabaseSchema.Property(item.GetType(), name);                if (prop is CustomProperty)                {                    if (!item.HasOriginalValue(name))                        item.SetOriginalValue(name, item.UserProperties[name]);                    item.UserProperties[name] = value;                }                else                {                    if (prop != null)                        try                        {                            var getter = prop.Getter();                            var oldvalue = getter != null ? getter.Invoke(item) : CoreUtils.GetPropertyValue(item, name);                            item.OnPropertyChanged(name, oldvalue, value);                            var setter = prop.Setter();                            if (setter != null && value != null)                                setter.Invoke(item, value);                            else                                CoreUtils.SetPropertyValue(item, name, value);                        }                        catch (Exception)                        {                            Logger.Send(LogType.Error, "",                                string.Format("Unable to Set Value for [{0}.{1}] (Value is {2})", item.GetType().Name, name, value));                        }                }                var current = GetValues(item, name, props);                foreach (var key in previous.Keys)                    if (previous[key] == null)                    {                        if (current[key] != null)                            changes[key] = current[key];                    }                    else                    {                        if (current[key] == null)                            changes[key] = current[key];                        else if (!object.Equals(previous[key], current[key]))                            changes[key] = current[key];                    }            }            return changes;        }        public static void UpdateEditorValue(BaseObject[] items, string name, object value, Dictionary<string, object?> changes)        {            var results = UpdateEditorValue(items, name, value);            foreach (var key in results.Keys)                changes[key] = results[key];        }        #endregion        #region Dynamic Grid Creation        public static IDynamicGrid CreateDynamicGrid(Type gridType, Type entityType)        {            var type = FindDynamicGrid(gridType, entityType);            return (Activator.CreateInstance(type) as IDynamicGrid)                ?? throw new ArgumentException("Argument must be a type of IDynamicGrid", nameof(gridType));        }        private static Dictionary<Type, Type[]> _dynamicGrids = new();        public static Type FindDynamicGrid(Type gridType, Type entityType)        {            if(!_dynamicGrids.TryGetValue(gridType, out var grids))            {                grids = CoreUtils.TypeList(                    AppDomain.CurrentDomain.GetAssemblies(),                    myType =>                        myType.IsClass                        && !myType.IsAbstract                        && !myType.IsGenericType                        && CoreUtils.IsSubclassOfRawGeneric(myType, gridType)                        && !myType.IsAssignableTo(typeof(ISpecificGrid))                ).ToArray();                _dynamicGrids[gridType] = grids;            }            var entityGrids = grids.Where(x => x.ContainsInheritedGenericType(entityType)).ToList();            var defaults = entityGrids.Where(x => x.IsAssignableTo(typeof(IDefaultGrid))).ToList();            if(defaults.Count > 0)            {                if(defaults.Count > 1)                {                    Logger.Send(LogType.Information, ClientFactory.UserID, $"Error: {defaults.Count} IDefaultGrid derivations for {gridType.Name} of {entityType.Name}");                }                return defaults.First();            }            return entityGrids.FirstOrDefault()                ?? gridType.MakeGenericType(entityType);        }        public static Window CreateGridWindow(string title, BaseDynamicGrid dynamicGrid)        {            dynamicGrid.Margin = new Thickness(5);            var window = new ThemableWindow { Title = title, Content = dynamicGrid };            (dynamicGrid as IDynamicGrid)!.Refresh(true, true);            return window;        }        public static Window CreateGridWindow(string title, Type entityType, Type? gridType = null)        {            gridType ??= typeof(DynamicGrid<>);            var grid = CreateDynamicGrid(gridType, entityType) as BaseDynamicGrid;            return CreateGridWindow(title, grid!);        }        public static Window CreateGridWindow<TGrid, TEntity>(string title)            where TEntity : BaseObject            where TGrid : IDynamicGrid        {            return CreateGridWindow(title, typeof(TEntity), typeof(TGrid));        }        public static Window CreateGridWindow<TEntity>(string title)            where TEntity : BaseObject        {            return CreateGridWindow(title, typeof(TEntity));        }        #endregion        public static void PopulateFormMenu<TEntityForm, TEntity, TEntityLink>(            ItemsControl menu,            Guid entityID,            Func<TEntity> loadEntity,            bool editOnAdd = false            )            where TEntityForm : EntityForm<TEntity, TEntityLink>, new()            where TEntity : Entity            where TEntityLink : IEntityLink<TEntity>, new()        {            var task = Task.Run(() =>            {                return new Client<TEntityForm>().Query(                    new Filter<TEntityForm>(x => x.Parent.ID).IsEqualTo(entityID),                    null).Rows.Select(x => x.ToObject<TEntityForm>()).ToList();            });            var addForm = new MenuItem { Header = "Add Form" };            addForm.Click += (o, e) =>            {                var entity = loadEntity();                var filter = LookupFactory.DefineFilter<TEntity, DigitalForm>(new TEntity[] { entity })                    ?? LookupFactory.DefineFilter<TEntityForm, DigitalForm>(Array.Empty<TEntityForm>());                var select = new MultiSelectDialog<DigitalForm>(                    filter,                    LookupFactory.DefineColumns<DigitalForm>()                        .Add(x => x.Description),                    false);                if(select.ShowDialog() == true)                {                    var digitalForm = select.Data().Rows.FirstOrDefault()?.ToObject<DigitalForm>();                    if(digitalForm is not null)                    {                        var form = new TEntityForm                        {                            Description = digitalForm.Description                        };                        form.Parent.ID = entityID;                        form.Form.ID = digitalForm.ID;                        if (editOnAdd)                        {                            if (DynamicFormEditWindow.EditDigitalForm(form, out var dataModel))                            {                                dataModel.Update(null);                            }                        }                        else                        {                            new Client<TEntityForm>().Save(form, "Added by user");                        }                    }                };            };            var manageForms = new MenuItem { Header = "Manage Forms..." };            manageForms.Click += (o, e) =>            {                var window = new ThemableWindow() { Title = $"Manage {typeof(TEntity).Name} Forms" };                var grid = new DynamicEntityFormGrid<TEntityForm, TEntity, TEntityLink>(loadEntity());                grid.Refresh(true, true);                grid.Margin = new Thickness(5);                window.Content = grid;                window.ShowDialog();            };            menu.Items.Add(addForm);            menu.Items.Add(new Separator());            menu.Items.Add(new MenuItem() { Header = "Loading...", IsEnabled = false });            menu.Items.Add(new Separator());            menu.Items.Add(manageForms);            task.ContinueWith((task) =>            {                var entityForms = task.Result;                menu.Items.Clear();                menu.Items.Add(addForm);                menu.Items.Add(new Separator());                if (entityForms.Any())                {                    foreach (var entityForm in entityForms)                    {                        var description = entityForm.Description;                        if (string.IsNullOrWhiteSpace(description))                        {                            description = entityForm.Form.Description;                        }                        var formItem = new MenuItem { Header = description };                        formItem.Click += (o, e) =>                        {                            if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel))                            {                                dataModel.Update(null);                            }                        };                        menu.Items.Add(formItem);                    }                }                else                {                    menu.Items.Add(new MenuItem() { Header = "No Forms", IsEnabled = false });                }                menu.Items.Add(new Separator());                menu.Items.Add(manageForms);            }, TaskScheduler.FromCurrentSynchronizationContext());        }    }}
 |