| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738 | 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.Core.Reports;using Syncfusion.Data.Extensions;using System.Diagnostics.CodeAnalysis;using System.Data;using System.Windows.Media;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, IList<Type>> _onetomanypages = new();    private static Dictionary<Type, IList<Type>> _manytomanytomanypages = new();    private static Dictionary<Type, IList<Tuple<Type, PropertyInfo>>> _enclosedlistpages = new();    private static Dictionary<Type, IList<Type>> _customeditorpages = new();    // 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.Entities.Where(x => x.HasInterface(typeof(IManyToMany<,>))).ToArray();        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.TryGetValue(type, out var pageTypes))        {            pageTypes = new List<Type>();            var maps = GetManyToManyTypes(type);            foreach (var map in maps)            {                if (ClientFactory.IsSupported(map))                {                    _allm2mpages ??= CoreUtils.Entities.Where(                            x => x.IsClass                                 && !x.IsGenericType                                 && x.HasInterface(typeof(IDynamicManyToManyGrid<,>)))                        .ToArray();                    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))                    );                    if (subtypes.Any())                    {                        pageTypes.Add(subtypes.First());                    }                    else                    {                        pageTypes.Add(typeof(DynamicManyToManyGrid<,>).MakeGenericType(map, type));                    }                }            }            _manytomanytomanypages[type] = pageTypes.ToArray();        }        pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x) as IDynamicEditorPage)!));    }    public static IEnumerable<Type> GetOneToManyTypes(Type type)    {        _allo2mtypes ??= CoreUtils.Entities.Where(            x => x.HasInterface(typeof(IOneToMany<>)) && !x.HasAttribute<ObsoleteAttribute>())            .ToArray();        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.TryGetValue(type, out var pageTypes))        {            pageTypes = new List<Type>();            var maps = GetOneToManyTypes(type);            foreach (var map in maps)            {                if (ClientFactory.IsSupported(map))                {                    _allo2mpages ??= CoreUtils.Entities.Where(                            x =>                                x.IsClass                                && !x.IsGenericType                                && x.HasInterface(typeof(IDynamicOneToManyGrid<,>))                                && !x.HasInterface<ISpecificGrid>())                        .ToArray();                    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)                        )                    );                    if (subtypes.Any())                    {                        pageTypes.Add(subtypes.First());                    }                    else                    {                        pageTypes.Add(typeof(DynamicOneToManyGrid<,>).MakeGenericType(type, map));                    }                }            }            _onetomanypages[type] = pageTypes.ToArray();        }        pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x) as IDynamicEditorPage)!));    }    public static void LoadCustomEditorPages(Type type, DynamicEditorPages pages)    {        if (!_customeditorpages.TryGetValue(type, out var pageTypes))        {            _allcepages ??= CoreUtils.Entities.Where(                    x => x.IsClass                         && !x.IsGenericType                         && x.HasInterface(typeof(IDynamicCustomEditorPage<>)))                .ToArray();            pageTypes = _allcepages.Where(x => x.GetInterfaces().Any(i =>                i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>) &&                i.GenericTypeArguments.First().Equals(type))).ToArray();            _customeditorpages[type] = pageTypes;        }        pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x) as IDynamicEditorPage)!));    }    public static void LoadEnclosedListPages(Type type, DynamicEditorPages pages)    {        if (!_enclosedlistpages.TryGetValue(type, out var pageTypes))        {            pageTypes = new List<Tuple<Type, PropertyInfo>>();            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.Entities.Where(                                        x => x.IsClass                                             && !x.IsGenericType                                             && x.HasInterface(typeof(IDynamicEnclosedListGrid<,>)))                                    .ToArray();                                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)                                    )                                );                                if (subtypes.Any())                                {                                    pageTypes.Add(new(subtypes.First(), property));                                }                                else                                {                                    subtypes = _alleltypes.Where(x => x.GetInterfaces().Any(i => (i.GenericTypeArguments.LastOrDefault()?.Equals(gentype) == true)));                                    if (subtypes.Any())                                    {                                        pageTypes.Add(new(subtypes.First().MakeGenericType(type), property));                                    }                                    else                                    {                                        try                                        {                                            pageTypes.Add(new(typeof(DynamicEnclosedListGrid<,>).MakeGenericType(type, gentype), property));                                        }                                        catch (Exception e)                                        {                                            Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));                                        }                                    }                                }                            }                        }                }            }            _enclosedlistpages[type] = pageTypes.ToArray();        }        pages.AddRange(pageTypes.Select(x => (Activator.CreateInstance(x.Item1, x.Item2) as IDynamicEditorPage)!));    }    #endregion    #region Columns    public static Columns<T> LoadEditorColumns<T>(Columns<T> additional)    {        var result = new Columns<T>(ColumnTypeFlags.EditorColumns);        foreach (var col in additional)            result.Add(col.Property);        foreach (var col in result.ToArray())        {            var prop = DatabaseSchema.Property(typeof(T), col.Property);            if (prop?.Editor is DataLookupEditor dataLookup)            {                foreach (var lookupColumn in LookupFactory.DefineLookupFilterColumns(typeof(T), prop.Name))                {                    result.Add(lookupColumn);                }            }        }        return result;    }    #endregion    #region Editor Values    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()).ToArray();        foreach (var item in items)        {            //Dictionary<String, object> previous = new Dictionary<string, object>();            var previous = CoreUtils.GetValues(item, 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 = CoreUtils.GetValues(item, props);            CoreUtils.MergeChanges(previous, current, changes);        }                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));    }    public static DynamicGrid<TEntity> CreateDynamicGrid<TEntity>(Type gridType)        where TEntity : BaseObject, new()    {        var type = FindDynamicGrid(gridType, typeof(TEntity));        return (Activator.CreateInstance(type) as DynamicGrid<TEntity>)            ?? throw new ArgumentException("Argument must be a type of IDynamicGrid", nameof(gridType));    }    private static Dictionary<Type, Type[]> _dynamicGrids = new();    public static bool TryFindDynamicGrid(Type gridType, Type entityType, [NotNullWhen(true)] out Type? grid)    {        if (!_dynamicGrids.TryGetValue(gridType, out var grids))        {            grids = CoreUtils.Entities.Where(                myType =>                    myType.IsClass                    && !myType.IsGenericType                    && myType.IsAssignableTo(typeof(IDynamicGrid))                    && !myType.IsAssignableTo(typeof(ISpecificGrid))            ).ToArray();            _dynamicGrids[gridType] = grids;        }        grids = grids.Where(x => x.IsSubclassOfRawGeneric(gridType)).ToArray();        var entityGrids = grids.Where(x =>        {            var baseGrid = x.GetSuperclassDefinition(typeof(DynamicGrid<>));            return baseGrid?.GenericTypeArguments[0] == 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}");            }            grid = defaults.First();            return true;        }        grid = entityGrids.FirstOrDefault();        return grid is not null;    }    public static Type FindDynamicGrid(Type gridType, Type entityType)    {        if(TryFindDynamicGrid(gridType, entityType, out var grid))        {            return grid;        }        return gridType.MakeGenericType(entityType);    }    public static Window CreateGridWindow(string title, IDynamicGrid dynamicGrid)    {        dynamicGrid.Margin = new Thickness(5);        var window = new ThemableWindow { Title = title, Content = dynamicGrid };        dynamicGrid.Refresh(true, true);        return window;    }    public static Window CreateGridWindow(string title, Type entityType, Type? gridType = null)    {        gridType ??= typeof(DynamicGrid<>);        var grid = CreateDynamicGrid(gridType, entityType);        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    #region Editing BaseObject    /// <summary>    /// Edit (using <see cref="DynamicItemsListGrid{T}"/>) a list of <see cref="BaseObject"/>s. Use for objects not saved in the database.    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="items"></param>    /// <param name="pageDataHandler"></param>    /// <param name="preloadPages"></param>    /// <returns></returns>    public static bool EditObjects<T>(T[] items, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)        where T : BaseObject, new()    {        var grid = new DynamicItemsListGrid<T>();        customiseGrid?.Invoke(grid);        return grid.EditItems(items, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);    }    /// <summary>    /// Edit (using <see cref="DynamicItemsListGrid{T}"/>) a <see cref="BaseObject"/>. Use for objects not saved in the database.    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="items"></param>    /// <param name="pageDataHandler"></param>    /// <param name="preloadPages"></param>    /// <returns></returns>    public static bool EditObject<T>(T item, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)        where T : BaseObject, new()    {        var grid = new DynamicItemsListGrid<T>();        customiseGrid?.Invoke(grid);        return grid.EditItems(new T[] { item }, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);    }    /// <summary>    /// Edit (using a grid sourced with <see cref="CreateDynamicGrid{T}(Type)"/>) a <typeparamref name="T"/>.    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="item"></param>    /// <param name="pageDataHandler"></param>    /// <param name="preloadPages"></param>    /// <param name="customiseGrid"></param>    /// <returns></returns>    public static bool EditEntity<T>(T item, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)        where T : Entity, new()    {        var grid = CreateDynamicGrid<T>(typeof(DynamicGrid<>));        customiseGrid?.Invoke(grid);        return grid.EditItems(new T[] { item }, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);    }    /// <summary>    /// Edit (using a grid sourced with <see cref="CreateDynamicGrid{T}(Type)"/>) a list of <typeparamref name="T"/>.    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="item"></param>    /// <param name="pageDataHandler"></param>    /// <param name="preloadPages"></param>    /// <param name="customiseGrid"></param>    /// <returns></returns>    public static bool EditEntities<T>(T[] items, Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false, Action<DynamicGrid<T>>? customiseGrid = null)        where T : Entity, new()    {        var grid = CreateDynamicGrid<T>(typeof(DynamicGrid<>));        customiseGrid?.Invoke(grid);        return grid.EditItems(items, PageDataHandler: pageDataHandler, PreloadPages: preloadPages);    }    #endregion    #region Drag + Drop    public static string DragFormat => typeof(DynamicGridDragFormat).FullName ?? "";    /// <summary>    /// Try to get data dragged from a <see cref="DynamicGrid{T}"/> from a <see cref="DragEventArgs"/>, returning <see langword="true"/>    /// if data was present.    /// </summary>    /// <param name="e"></param>    public static bool TryGetDropData(        DragEventArgs e,        [NotNullWhen(true)] out Type? type,        [NotNullWhen(true)] out CoreTable? table)    {        if (e.Data.GetDataPresent(DragFormat))        {            var data = e.Data.GetData(DragFormat) as DynamicGridDragFormat;            if (data is not null)            {                table = new CoreTable();                foreach (var column in data.Table.Columns)                {                    if (column is DataColumn dataColumn)                    {                        table.Columns.Add(new CoreColumn { ColumnName = dataColumn.ColumnName.Replace('_', '.'), DataType = dataColumn.DataType });                    }                }                foreach (var row in data.Table.Rows)                {                    if (row is DataRow dataRow)                    {                        var coreRow = table.NewRow();                        coreRow.LoadValues(dataRow.ItemArray);                        table.Rows.Add(coreRow);                    }                }                type = data.Entity;                return true;            }        }        table = null;        type = null;        return false;    }    #endregion    #region Style        public static Brush SelectionBackground { get; set; } = new SolidColorBrush(Colors.Black);    public static Brush SelectionForeground { get; set; } = new SolidColorBrush(Colors.Silver);    public static Brush FilterBackground { get; set; } = new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0xE9, 0xF7, 0xC9));    #endregion    public static void PopulateFormMenu<TEntityForm, TEntity, TEntityLink>(        ItemsControl menu,        Guid entityID,        Func<TEntity> loadEntity,        bool editOnAdd = false,        DynamicFormEditWindow.CustomiseDynamicFormEditWindow? customiseEditor = null)        where TEntityForm : EntityForm<TEntity, TEntityLink, TEntityForm>, 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.DefineChildFilter<TEntity, DigitalForm>(new TEntity[] { entity })                ?? LookupFactory.DefineLookupFilter<TEntityForm, DigitalForm, DigitalFormLink>(x => x.Form, Array.Empty<TEntityForm>());            var select = new MultiSelectDialog<DigitalForm>(                filter,                LookupFactory.DefineLookupColumns<TEntityForm, DigitalForm, DigitalFormLink>(x => x.Form).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, customise: customiseEditor))                        {                            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 = $"{entityForm.Number} : {description}" };                    formItem.Click += (o, e) =>                    {                        if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel, customise: customiseEditor))                        {                            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());    }}
 |