Browse Source

Added an option to exclude ID from Columns.Default(); reworked dynamiceditorform to allow embedded editor forms. DynamicEditorForm is now simply a container window;

Kenric Nugteren 2 years ago
parent
commit
09ab362281
38 changed files with 1122 additions and 751 deletions
  1. 2 1
      InABox.Core/Column.cs
  2. 3 1
      InABox.Core/DataTable.cs
  3. 1 1
      InABox.Core/ICoreRow.cs
  4. 10 0
      InABox.Core/Security/Security.cs
  5. 2 2
      InABox.DynamicGrid/AuditGrid.cs
  6. 7 2
      InABox.DynamicGrid/BaseDynamicGrid.cs
  7. 1 1
      InABox.DynamicGrid/DynamicCrossJoinGrid.cs
  8. 9 10
      InABox.DynamicGrid/DynamicDataGrid.cs
  9. 4 3
      InABox.DynamicGrid/DynamicEditorControl.cs
  10. 1 23
      InABox.DynamicGrid/DynamicEditorForm.xaml
  11. 87 345
      InABox.DynamicGrid/DynamicEditorForm.xaml.cs
  12. 195 174
      InABox.DynamicGrid/DynamicEditorGrid.xaml.cs
  13. 3 3
      InABox.DynamicGrid/DynamicEnclosedListGrid.cs
  14. 1 1
      InABox.DynamicGrid/DynamicEntityFormGrid.cs
  15. 2 2
      InABox.DynamicGrid/DynamicExportMappingGrid.cs
  16. 36 29
      InABox.DynamicGrid/DynamicGrid.cs
  17. 1 1
      InABox.DynamicGrid/DynamicGridFilterGrid.cs
  18. 9 5
      InABox.DynamicGrid/DynamicGridStyle.cs
  19. 7 7
      InABox.DynamicGrid/DynamicImportGrid.cs
  20. 24 18
      InABox.DynamicGrid/DynamicImportMappingGrid.cs
  21. 2 2
      InABox.DynamicGrid/DynamicManyToManyGrid.cs
  22. 2 2
      InABox.DynamicGrid/DynamicMultiEditWindow.xaml
  23. 4 3
      InABox.DynamicGrid/DynamicMultiEditWindow.xaml.cs
  24. 2 2
      InABox.DynamicGrid/DynamicOneToManyGrid.cs
  25. 18 14
      InABox.DynamicGrid/DynamicTreeView.cs
  26. 77 1
      InABox.DynamicGrid/Editors/DocumentConfirm.xaml.cs
  27. 86 70
      InABox.DynamicGrid/Editors/DocumentEditorControl.cs
  28. 3 3
      InABox.DynamicGrid/Editors/JsonEditorControl.cs
  29. 7 6
      InABox.DynamicGrid/Editors/MultiLookupEditorControl.cs
  30. 17 11
      InABox.DynamicGrid/Editors/PopupEditorControl.cs
  31. 2 4
      InABox.DynamicGrid/Editors/RichTextEditor.xaml.cs
  32. 34 0
      InABox.DynamicGrid/EmbeddedDynamicEditorForm.xaml
  33. 414 0
      InABox.DynamicGrid/EmbeddedDynamicEditorForm.xaml.cs
  34. 1 1
      InABox.DynamicGrid/FormDesigner/DynamicFormControlGrid.cs
  35. 2 1
      InABox.DynamicGrid/IDynamicGrid.cs
  36. 1 1
      InABox.DynamicGrid/ScriptModel.cs
  37. 44 1
      inabox.wpf/ImageUtils.cs
  38. 1 0
      inabox.wpf/InABox.Wpf.csproj

+ 2 - 1
InABox.Core/Column.cs

@@ -102,6 +102,7 @@ namespace InABox.Core
     public enum ColumnType
     {
         ExcludeVisible,
+        ExcludeID,
         IncludeOptional,
         IncludeForeignKeys,
         IncludeLinked,
@@ -313,7 +314,7 @@ namespace InABox.Core
                 return this;
             }
 
-            if (typeof(T).IsSubclassOf(typeof(Entity)))
+            if (typeof(T).IsSubclassOf(typeof(Entity)) && !types.Contains(ColumnType.ExcludeID))
                 columns.Add(new Column<T>("ID"));
             
             for  (int iCol=0; iCol<props.Count; iCol++)

+ 3 - 1
InABox.Core/DataTable.cs

@@ -76,7 +76,7 @@ namespace InABox.Core
                 Set(columnName, value);
         }
 
-        public object ToObject(Type t)
+        public BaseObject ToObject(Type t)
         {
             var entity = (Activator.CreateInstance(t) as BaseObject)!;
             entity.SetObserving(false);
@@ -584,6 +584,8 @@ namespace InABox.Core
             return result;
         }
 
+        public IEnumerable<BaseObject> ToObjects(Type T)
+            => Rows.Select(x => x.ToObject(T));
         public IEnumerable<T> ToObjects<T>() where T : BaseObject, new()
             => Rows.Select(x => x.ToObject<T>());
         public List<T> ToList<T>() where T : BaseObject, new()

+ 1 - 1
InABox.Core/ICoreRow.cs

@@ -17,7 +17,7 @@ namespace InABox.Core
         void Set<T>(string columnname, T value);
         void Set<TSource, TType>(Expression<Func<TSource, TType>> expression, TType value);
         Dictionary<string, object> ToDictionary(string[] exclude);
-        object ToObject(Type t);
+        BaseObject ToObject(Type t);
         T ToObject<T>() where T : BaseObject, new();
     }
 }

+ 10 - 0
InABox.Core/Security/Security.cs

@@ -160,11 +160,21 @@ namespace InABox.Core
             return ClientFactory.IsSupported<TEntity>() && IsAllowed<AutoSecurityDescriptor<TEntity, CanView<TEntity>>>();
         }
 
+        public static bool CanEdit(Type TEntity, Guid userGuid, Guid securityId)
+        {
+            return ClientFactory.IsSupported(TEntity) &&
+                IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanEdit<>).MakeGenericType(TEntity)), userGuid, securityId);
+        }
         public static bool CanEdit<TEntity>(Guid userGuid, Guid securityId) where TEntity : Entity, new()
         {
             return ClientFactory.IsSupported<TEntity>() && IsAllowed<AutoSecurityDescriptor<TEntity, CanEdit<TEntity>>>(userGuid, securityId);
         }
 
+        public static bool CanEdit(Type TEntity)
+        {
+            return ClientFactory.IsSupported(TEntity) &&
+                IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanEdit<>).MakeGenericType(TEntity)));
+        }
         public static bool CanEdit<TEntity>() where TEntity : Entity, new()
         {
             return ClientFactory.IsSupported<TEntity>() && IsAllowed<AutoSecurityDescriptor<TEntity, CanEdit<TEntity>>>();

+ 2 - 2
InABox.DynamicGrid/AuditGrid.cs

@@ -13,8 +13,8 @@ namespace InABox.DynamicGrid
 
         public Guid EntityID { get; set; }
 
-        protected override void Reload(Filters<AuditTrail> criteria, Columns<AuditTrail> columns, ref SortOrder<AuditTrail> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<AuditTrail> criteria, Columns<AuditTrail> columns, ref SortOrder<AuditTrail>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             criteria.Add(new Filter<AuditTrail>(x => x.EntityID).IsEqualTo(EntityID));
             sort = new SortOrder<AuditTrail>(x => x.Timestamp, SortDirection.Descending);

+ 7 - 2
InABox.DynamicGrid/BaseDynamicGrid.cs

@@ -47,7 +47,7 @@ namespace InABox.DynamicGrid
 
     public abstract class BaseDynamicGrid<T> : BaseDynamicGrid, IDynamicGrid where T : BaseObject, new()
     {
-        public delegate void EntitySaveEvent(DynamicEditorForm editor, T[] items);
+        public delegate void EntitySaveEvent(IDynamicEditorForm editor, T[] items);
 
         public delegate void ValidateEvent(object sender, T[] items, List<string> errors);
 
@@ -77,13 +77,17 @@ namespace InABox.DynamicGrid
 
         public List<Expression<Func<T, object?>>> HiddenColumns { get; }
 
+        public void InitialiseEditorForm(IDynamicEditorForm editor, object[] items, Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false)
+        {
+            InitialiseEditorForm(editor, items.Cast<T>().ToArray(), pageDataHandler, preloadPages);
+        }
         public virtual bool EditItems(object[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)
         {
             var values = items.Select(x => (T)x).ToArray();
             return EditItems(values, PageDataHandler, PreloadPages);
         }
 
-        public abstract bool DirectEdit(CoreTable data);
+        //public abstract bool DirectEdit(CoreTable data);
 
         public FluentList<DynamicGridOption> Options { get; }
         public DynamicGridColumns MasterColumns { get; protected set; }
@@ -124,6 +128,7 @@ namespace InABox.DynamicGrid
             return OnGetStyle != null ? OnGetStyle(row, style) : style;
         }
 
+        public abstract void InitialiseEditorForm(IDynamicEditorForm editor, T[] items, Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false);
         public abstract bool EditItems(T[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false);
 
         public Filter<T>? DefineFilter()

+ 1 - 1
InABox.DynamicGrid/DynamicCrossJoinGrid.cs

@@ -31,7 +31,7 @@ namespace InABox.DynamicGrid
             columns.RemoveAll(x => x.ColumnName.StartsWith(prefix));
         }
 
-        protected override void Reload(Filters<TEntity> criteria, Columns<TEntity> columns, ref SortOrder<TEntity> sort, Action<CoreTable?, Exception?> action)
+        protected override void Reload(Filters<TEntity> criteria, Columns<TEntity> columns, ref SortOrder<TEntity>? sort, Action<CoreTable?, Exception?> action)
         {
             var filter = new Filter<TEntity>();
             filter.Expression = CoreUtils.ExtractMemberExpression<TEntity, Guid>(LeftMapping);

+ 9 - 10
InABox.DynamicGrid/DynamicDataGrid.cs

@@ -24,6 +24,8 @@ namespace InABox.DynamicGrid
         /// the name of <typeparamref name="TEntity"/> is used as a default.
         /// </summary>
         string? ColumnsTag { get; set; }
+
+        IColumns LoadEditorColumns();
     }
 
     public class DynamicDataGrid<TEntity> : DynamicGrid<TEntity>, IDynamicDataGrid where TEntity : Entity, IRemotable, IPersistent, new()
@@ -49,11 +51,6 @@ namespace InABox.DynamicGrid
 
         private Column<TEntity>[] FilterColumns;
 
-        /// <summary>
-        /// TEntity is not an AutoEntity, and thus has an ID field to link on.
-        /// </summary>
-        private bool IsEntity;
-
         public DynamicDataGrid() : base()
         {
             FilterBtn = AddButton("", Properties.Resources.filter.AsBitmapImage(), DoFilter);
@@ -121,12 +118,10 @@ namespace InABox.DynamicGrid
                 {
                     FilterColumns = Array.Empty<Column<TEntity>>();
                 }
-                IsEntity = false;
             }
             else
             {
                 FilterColumns = new[] { new Column<TEntity>(x => x.ID) };
-                IsEntity = true;
             }
 
             foreach(var column in FilterColumns)
@@ -135,7 +130,7 @@ namespace InABox.DynamicGrid
             }
         }
 
-        protected override void CustomiseEditorForm(DynamicEditorForm form)
+        protected override void CustomiseEditorForm(IDynamicEditorForm form)
         {
             base.CustomiseEditorForm(form);
 
@@ -259,6 +254,8 @@ namespace InABox.DynamicGrid
             base.Refresh(reloadcolumns, reloaddata);
         }
 
+        IColumns IDynamicDataGrid.LoadEditorColumns() => LoadEditorColumns();
+
         public Columns<TEntity> LoadEditorColumns()
         {
             var result = new Columns<TEntity>().Default(
@@ -514,8 +511,10 @@ namespace InABox.DynamicGrid
         protected virtual void GenerateColumns(DynamicGridColumns columns)
         {
             var cols = new Columns<TEntity>().Default(Options.Contains(DynamicGridOption.DirectEdit)
-                ? new[] { ColumnType.IncludeForeignKeys }
-                : new ColumnType[] { ColumnType.IncludeLinked, ColumnType.IncludeNestedLinks, ColumnType.IncludeFormulae, ColumnType.IncludeAggregates });
+                ? new[] { ColumnType.IncludeForeignKeys, ColumnType.ExcludeID }
+                : new ColumnType[] { 
+                    ColumnType.IncludeLinked, ColumnType.IncludeNestedLinks, ColumnType.IncludeFormulae,
+                    ColumnType.IncludeAggregates, ColumnType.ExcludeID });
             if (cols != null)
             {
                 foreach (var col in cols.Items)

+ 4 - 3
InABox.DynamicGrid/DynamicEditorControl.cs

@@ -32,7 +32,7 @@ namespace InABox.DynamicGrid
 
         void SetValue(object value);
 
-        object GetValue();
+        object? GetValue();
     }
 
     public interface ILookupEditorControl : IDynamicEditorControl
@@ -45,6 +45,7 @@ namespace InABox.DynamicGrid
 
     public interface IPopupEditorControl
     {
+        Dictionary<string, string> OtherColumns { get; }
         event OnDefineFilter OnDefineFilter;
     }
 
@@ -113,7 +114,7 @@ namespace InABox.DynamicGrid
 
         public abstract void SetValue(object? value);
 
-        public abstract object GetValue();
+        public abstract object? GetValue();
 
         public new bool IsEnabled
         {
@@ -245,7 +246,7 @@ namespace InABox.DynamicGrid
             UpdateValue(value != null ? (T)value : default);
         }
 
-        public override object GetValue()
+        public override object? GetValue()
         {
             return RetrieveValue();
         }

+ 1 - 23
InABox.DynamicGrid/DynamicEditorForm.xaml

@@ -33,27 +33,5 @@
         </local:MyObservableCollection>-->
     </syncfusion:ChromelessWindow.Resources>
 
-    <Grid x:Name="LayoutGrid">
-
-        <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="*" />
-            <ColumnDefinition Width="80" />
-            <ColumnDefinition Width="80" />
-        </Grid.ColumnDefinitions>
-        <Grid.RowDefinitions>
-            <RowDefinition Height="*" />
-            <RowDefinition Height="40" />
-        </Grid.RowDefinitions>
-
-        <local:DynamicEditorGrid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="5,5,5,0"
-                                 HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
-
-        <StackPanel Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" Orientation="Horizontal"
-                    x:Name="Buttons">
-            <!--<Button x:Name="ShowHelp" Content="Help" HorizontalAlignment="Left" Width="80" Grid.Row="2" Grid.Column="0" Margin="5,5,5,5" Click="ShowHelp_Click"/>-->
-        </StackPanel>
-        <Button x:Name="OKButton" Content="OK" Grid.Row="1" Grid.Column="1" Margin="0,5,5,5" Click="OKButton_Click" />
-        <Button x:Name="CancelButton" Content="Cancel" Grid.Row="1" Grid.Column="2" Margin="0,5,5,5"
-                Click="CancelButton_Click" />
-    </Grid>
+    <local:EmbeddedDynamicEditorForm x:Name="Form"/>
 </wpf:ThemableChromelessWindow>

+ 87 - 345
InABox.DynamicGrid/DynamicEditorForm.xaml.cs

@@ -28,6 +28,45 @@ namespace InABox.DynamicGrid
 
     public interface IDynamicEditorForm
     {
+        public delegate Document? FindDocumentEvent(string FileName);
+        public delegate Document? GetDocumentEvent(Guid id);
+        public delegate void SaveDocumentEvent(Document document);
+
+        public event OnValidateData? OnValidateData;
+
+        public event OnCustomiseColumns? OnCustomiseColumns;
+        public event OnDefineFilter? OnDefineFilter;
+
+        public event OnDefineLookup? OnDefineLookups;
+        public event OnLookupsDefined? OnLookupsDefined;
+
+        public event DefineEditorEventHandler? OnDefineEditor;
+        public event OnFormCustomiseEditor? OnFormCustomiseEditor;
+        public event OnReconfigureEditors? ReconfigureEditors;
+
+        public event EditorValueChangedHandler? OnEditorValueChanged;
+
+        public event GetDocumentEvent? OnGetDocument;
+        public event FindDocumentEvent? OnFindDocument;
+        public event SaveDocumentEvent? OnSaveDocument;
+
+        public event OnSelectPage? OnSelectPage;
+
+        public event DynamicGridSaveEvent? OnSaveItem;
+
+
+        DynamicEditorPages? Pages { get; }
+
+        bool ReadOnly { get; set; }
+
+        BaseObject[] Items { get; set; }
+
+        void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+            Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false);
+
+        IDynamicEditorControl FindEditor(string columnName);
+
+        void UnloadEditorPages(bool saved);
     }
 
     public class UtilityItem
@@ -78,312 +117,73 @@ namespace InABox.DynamicGrid
     /// </summary>
     public partial class DynamicEditorForm : ThemableChromelessWindow, IDynamicEditorForm
     {
-        public delegate Document? FindDocumentEvent(string FileName);
+        #region IDynamicEditorForm
 
-        public delegate Document? GetDocumentEvent(Guid id);
-
-        public delegate void SaveDocumentEvent(Document document);
-
-        private BaseObject[] _items;
-
-        public DynamicEditorGrid Editor;
-
-        public DynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
-            Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)
-        {
-            ReadOnly = false;
-
-            InitializeComponent();
-
-            //this.Loaded += new RoutedEventHandler(ConfigureSystemMenu);
-
-            var grid = Content as Grid;
-            Editor = grid.Children.OfType<DynamicEditorGrid>().FirstOrDefault();
-
-            Editor.UnderlyingType = type;
-
-            Editor.OnCustomiseColumns += Editor_OnCustomiseColumns;
-
-            Editor.OnDefineFilter += (sender, t) => OnDefineFilter?.Invoke(sender, t);
+        public bool ReadOnly { get => Form.ReadOnly; set => Form.ReadOnly = value; }
+        public BaseObject[] Items { get => Form.Items; set => Form.Items = value; }
+        public DynamicEditorPages? Pages { get => Form.Pages; }
 
-            Editor.OnEditorCreated += Editor_OnEditorCreated;
 
-            Editor.OnLoadPage += page => { page.Load(Items.First(), PageDataHandler); };
+        public event OnValidateData? OnValidateData { add => Form.OnValidateData += value; remove => Form.OnValidateData -= value; }
 
-            Editor.OnSelectPage += (tab, items) => { OnSelectPage?.Invoke(tab, items); };
+        public event OnCustomiseColumns? OnCustomiseColumns { add => Form.OnCustomiseColumns += value; remove => Form.OnCustomiseColumns -= value; }
+        public event OnDefineFilter? OnDefineFilter { add => Form.OnDefineFilter += value; remove => Form.OnDefineFilter -= value; }
 
-            Editor.PreloadPages = PreloadPages;
-
-            Editor.OnUnloadPage += (page, saved) =>
-            {
-                if (!saved)
-                    page.BeforeSave(Items.First());
-                else
-                    page.AfterSave(Items.First());
-            };
-
-            //Editor.OnGetPropertyInfo += (o, c) => { return CoreUtils.GetProperty(_item.GetType(), c); };
-
-            Editor.ReconfigureEditors += g => { ReconfigureEditors?.Invoke(g); };
-
-            Editor.OnGetEditor += c =>
-            {
-                if (_items != null && _items.Any())
-                {
-                    var property = DatabaseSchema.Property(type, c.ColumnName);
-                    if (property == null) return new NullEditor();
-
-                    if (property.Editor is NullEditor)
-                        return property.Editor;
-
-                    BaseEditor editor;
-                    if (property is CustomProperty)
-                    {
-                        editor = property.Editor.CloneEditor();
-                    }
-                    else
-                    {
-                        editor = OnDefineEditor?.Invoke(_items[0], c) ?? c.Editor.CloneEditor();
-                        var propEditor = property.Editor;
-                        editor.Page = propEditor.Page;
-                        editor.Caption = propEditor.Caption;
-                    }
-
-
-                    //defaultEditor.EditorSequence
-
-                    //EditorUtils.GetPropertyEditor(type, property, defaultEditor);
-
-                    /*BaseEditor editor = new NullEditor();
-                    var caption = "";
-                    var page = "";
-
-                    try
-                    {
-                        var comps = c.ColumnName.Split('.');
-                        for (var i = 0; i < comps.Length; i++)
-                        {
-                            var column = string.Join(".", comps.Take(i + 1));
-                            var prop = CoreUtils.GetProperty(type, column);
-
-                            if (column.Equals(c.ColumnName))
-                            {
-                                if (OnDefineEditor != null)
-                                    editor = OnDefineEditor(_items[0], c);
-                                else
-                                    editor = c.Editor != null ? c.Editor : new NullEditor();
-                            }
-                            else
-                            {
-                                var pedit = prop.GetEditor();
-                                if (pedit is NullEditor)
-                                    return pedit;
-                            }
-
-                            editor = editor == null ? new NullEditor() : editor.Clone() as BaseEditor;
-
-                            var capattr = prop.GetCustomAttribute<Caption>();
-                            var subcap = capattr != null ? capattr.Text : comps[i];
-                            var path = capattr != null ? capattr.IncludePath : true;
-                            if (!string.IsNullOrWhiteSpace(subcap))
-                                caption = string.IsNullOrWhiteSpace(caption) || path == false ? subcap : string.Format("{0} {1}", caption, subcap);
-
-                            if (string.IsNullOrWhiteSpace(page))
-                            {
-                                var pageattr = prop.GetCustomAttribute<EditorSequence>();
-                                if (pageattr != null)
-                                    page = pageattr.Page;
-                            }
-                        }
-
-                        editor.Caption = caption;
-                        editor.Page = page;
-                    }
-                    catch (Exception e)
-                    {
-                        var dmprop = DatabaseSchema.Property(_items[0].GetType(), c.ColumnName);
-                        if (dmprop is CustomProperty)
-                        {
-                            editor = dmprop.Editor.Clone() as BaseEditor;
-                            editor.Caption = dmprop.Caption;
-                            editor.Page = string.IsNullOrWhiteSpace(dmprop.Page) ? "Custom Fields" : dmprop.Page;
-                        }
-                    }*/
-
-                    if (ReadOnly && editor.Editable.Equals(Editable.Enabled))
-                        editor.Editable = Editable.Disabled;
-                    return editor;
-                }
-
-                return new NullEditor();
-            };
-
-            Editor.OnGridCustomiseEditor += (sender, column, editor) => OnFormCustomiseEditor?.Invoke(this, Items, column, editor);
-
-            Editor.OnGetSequence += c => CoreUtils.GetPropertySequence(_items.First().GetType(), c.ColumnName);
-
-            Editor.OnGetPropertyValue += (o, c) =>
-            {
-                if (!_items.Any())
-                    return null;
-
-                object? result;
-                try
-                {
-                    result = CoreUtils.GetPropertyValue(_items.First(), c);
-                }
-                catch
-                {
-                    result = _items.First().UserProperties.ContainsKey(c) ? _items.First().UserProperties[c] : null;
-                }
+        public event OnDefineLookup? OnDefineLookups { add => Form.OnDefineLookups += value; remove => Form.OnDefineLookups -= value; }
+        public event OnLookupsDefined? OnLookupsDefined { add => Form.OnLookupsDefined += value; remove => Form.OnLookupsDefined -= value; }
 
-                if (result == null)
-                    return null;
-
-                foreach (var _item in _items)
-                {
-                    object? curvalue;
-                    try
-                    {
-                        curvalue = CoreUtils.GetPropertyValue(_item, c);
-                    }
-                    catch
-                    {
-                        curvalue = _item.UserProperties.ContainsKey(c) ? _item.UserProperties[c] : null;
-                    }
-
-                    if (curvalue == null)
-                        return null;
-
-                    if (!curvalue.Equals(result))
-                        return null;
-                }
+        public event DefineEditorEventHandler? OnDefineEditor { add => Form.OnDefineEditor += value; remove => Form.OnDefineEditor -= value; }
+        public event OnFormCustomiseEditor? OnFormCustomiseEditor { add => Form.OnFormCustomiseEditor += value; remove => Form.OnFormCustomiseEditor -= value; }
+        public event OnReconfigureEditors? ReconfigureEditors { add => Form.ReconfigureEditors += value; remove => Form.ReconfigureEditors -= value; }
+        
+        public event EditorValueChangedHandler? OnEditorValueChanged { add => Form.OnEditorValueChanged += value; remove => Form.OnEditorValueChanged -= value; }
 
-                return result;
-            };
+        public event IDynamicEditorForm.GetDocumentEvent? OnGetDocument { add => Form.OnGetDocument += value; remove => Form.OnGetDocument -= value; }
+        public event IDynamicEditorForm.FindDocumentEvent? OnFindDocument { add => Form.OnFindDocument += value; remove => Form.OnFindDocument -= value; }
+        public event IDynamicEditorForm.SaveDocumentEvent? OnSaveDocument { add => Form.OnSaveDocument += value; remove => Form.OnSaveDocument -= value; }
 
-            Editor.OnSetPropertyValue += (o, c, v) =>
-            {
-                foreach (var _item in _items)
-                    if (_item.UserProperties.ContainsKey(c))
-                        _item.UserProperties[c] = v;
-                    else
-                        CoreUtils.SetPropertyValue(_item, c, v);
-            };
+        public event OnSelectPage? OnSelectPage { add => Form.OnSelectPage += value; remove => Form.OnSelectPage -= value; }
 
-            Editor.OnEditorValueChanged += EditorValueChanged;
+        public event DynamicGridSaveEvent? OnSaveItem { add => Form.OnSaveItem += value; remove => Form.OnSaveItem -= value; }
 
-            Editor.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
-            Editor.OnLookupsDefined += sender => { OnLookupsDefined?.Invoke(sender); };
 
-            Editor.OnGetDocument += id => { return OnGetDocument?.Invoke(id); };
-            Editor.OnSaveDocument += doc => { OnSaveDocument?.Invoke(doc); };
-            Editor.OnFindDocument += file => { return OnFindDocument?.Invoke(file); };
+        public IDynamicEditorControl FindEditor(string columnName) => Form.FindEditor(columnName);
 
-            Editor.GetItems += () => _items;
+        public void UnloadEditorPages(bool saved) => Form.UnloadEditorPages(saved);
 
-            Pages = pages;
+        #endregion
 
-            if (Pages == null || Pages.Count == 0)
-                Editor.Margin = new Thickness(5, 5, 5, 0);
+        public DynamicEditorForm()
+        {
+            InitializeComponent();
+            
+            //this.Loaded += new RoutedEventHandler(ConfigureSystemMenu);
 
-            if (buttons != null)
-                foreach (var button in buttons)
-                {
-                    var btn = new Button();
-                    UpdateButton(btn, button.Image, button.Name);
-                    btn.Tag = button;
-                    btn.Margin = new Thickness(5, 5, 0, 5);
-                    btn.Padding = new Thickness(5, 0, 5, 0);
-                    btn.Click += Btn_Click;
-                    Buttons.Children.Add(btn);
-                    button.Button = btn;
-                    button.Form = this;
-                }
+            Form.OnEditorCreated += Editor_OnEditorCreated;
+            Form.OnOK += Form_OnOK;
+            Form.OnCancel += Form_OnCancel;
         }
 
-        public DynamicEditorPages? Pages { get; }
-
-        public BaseObject[] Items
+        public DynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+            Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false): this()
         {
-            get => _items;
-            set
-            {
-                _items = value;
-                UtilityViewModel.Slug = Items != null ? Items.Any() ? Items.First().GetType().EntityName().Split('.').Last() : "" : "";
-                Editor.Load(_items.First().GetType().EntityName(), Pages);
-            }
+            Setup(type, pages, buttons, pageDataHandler, preloadPages);
         }
 
-        public bool ReadOnly { get; set; }
-
-        public OnValidateData? OnValidateData;
-
-        public event OnCustomiseColumns? OnCustomiseColumns;
-
-        public event OnDefineFilter? OnDefineFilter;
-
-        public event DefineEditorEventHandler? OnDefineEditor;
-
-        public event OnFormCustomiseEditor? OnFormCustomiseEditor;
-
-        public event OnReconfigureEditors? ReconfigureEditors;
-
-        //public delegate void EditorValueChangedHandler(object sender, String name, object value, List<String> changes);
-        //public event EditorValueChangedHandler OnEditorValueChanged;
-
-        public event EditorValueChangedHandler? OnEditorValueChanged;
-
-        //public event DefineFilter OnDefineFilter;
-
-        public event OnDefineLookup? OnDefineLookups;
-
-        public event OnLookupsDefined? OnLookupsDefined;
-        public event GetDocumentEvent? OnGetDocument;
-        public event FindDocumentEvent? OnFindDocument;
-        public event SaveDocumentEvent? OnSaveDocument;
-
-        public event OnSelectPage? OnSelectPage;
-
-        public event DynamicGridSaveEvent? OnSaveItem;
-
-        public void UnloadEditorPages(DynamicEditorPages pages, object item, bool saved)
+        public void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+            Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false)
         {
-            Editor.UnloadPages(saved);
+            Form.Setup(type, pages, buttons, pageDataHandler, preloadPages);
         }
 
-        protected void UpdateButton(Button button, BitmapImage? image, string text)
+        private void Form_OnCancel()
         {
-            var stackPnl = new StackPanel();
-            stackPnl.Orientation = Orientation.Horizontal;
-            //stackPnl.Margin = new Thickness(2);
-
-            if (image != null)
-            {
-                var img = new Image();
-                img.Source = image;
-                img.Margin = new Thickness(2);
-                stackPnl.Children.Add(img);
-            }
-
-            if (!string.IsNullOrEmpty(text))
-            {
-                var lbl = new Label();
-                lbl.Content = text;
-                lbl.VerticalAlignment = VerticalAlignment.Stretch;
-                lbl.VerticalContentAlignment = VerticalAlignment.Center;
-                lbl.Margin = new Thickness(2, 0, 5, 0);
-                stackPnl.Children.Add(lbl);
-            }
-
-            button.Content = stackPnl;
+            DialogResult = false;
         }
 
-        private Dictionary<string, object?> EditorValueChanged(object sender, string name, object value)
+        private void Form_OnOK()
         {
-            if (OnEditorValueChanged != null)
-                return OnEditorValueChanged(sender, name, value);
-            return DynamicGridUtils.UpdateEditorValue(_items, name, value);
+            DialogResult = true;
         }
 
         private void Editor_OnEditorCreated(object sender, double height, double width)
@@ -395,8 +195,8 @@ namespace InABox.DynamicGrid
 
             var desiredheight = height + spareheight;
             var desiredwidth = width + sparewidth;
-            if (Pages != null)
-                foreach (var page in Pages)
+            if (Form.Pages != null)
+                foreach (var page in Form.Pages)
                 {
                     if (desiredheight < page.MinimumSize().Height)
                         desiredheight = page.MinimumSize().Height;
@@ -413,74 +213,16 @@ namespace InABox.DynamicGrid
             Left = screen.DeviceBounds.Left + (screen.DeviceBounds.Width - Width) / 2.0F;
             Top = screen.DeviceBounds.Top + (screen.DeviceBounds.Height - Height) / 2.0F;
 
-            Editor.VerticalAlignment = VerticalAlignment.Stretch;
-            Editor.HorizontalAlignment = HorizontalAlignment.Stretch;
-
-            var scaption = _items[0].GetType().GetCaption();
+            var scaption = Form.Items[0].GetType().GetCaption();
             Title = "Edit " + scaption.SplitCamelCase();
-            if (Editor.IsCustomLayout)
-                Title = Title + "*";
-
-            OKButton.IsEnabled = !ReadOnly;
-        }
-        
-        private DynamicGridColumns Editor_OnCustomiseColumns(object sender, DynamicGridColumns? source)
-        {
-            var columns = new DynamicGridColumns();
-            if (_items != null && _items.Any())
-                columns.ExtractColumns(_items.First().GetType(), "");
-            if (OnCustomiseColumns != null)
-                columns = OnCustomiseColumns.Invoke(this, columns);
-            return columns;
-        }
-
-        private void Btn_Click(object sender, RoutedEventArgs e)
-        {
-            var button = (Button)sender;
-            var deb = (DynamicEditorButton)button.Tag;
-            deb.Click();
-        }
-
-        private void OKButton_Click(object sender, RoutedEventArgs e)
-        {
-            var errors = OnValidateData?.Invoke(this, Items);
-
-            if (errors != null && errors.Any())
-            {
-                MessageBox.Show(
-                    string.Format("The following errors have been found with your data!\nPlease correct them and try again.\n\n- {0}",
-                        string.Join("\n- ", errors)), "Validation Error");
-                return;
-            }
-
-            // Don't Commit the changes here, because we want to refer back to thos changes when we save the item
-            // to trigger specific processes in the database
-            DialogResult = true;
-            //Close();
-        }
-
-        private void CancelButton_Click(object sender, RoutedEventArgs e)
-        {
-            // However, if we cancel the edits, then we can safely revert the items back to their original (loaded) state
-            foreach (var item in _items)
-                item.CancelChanges();
-            DialogResult = false;
-            //Close();
-        }
-
-        public bool TryFindEditor(string columnname, [NotNullWhen(true)] out IDynamicEditorControl? editor)
-        {
-            return Editor.TryFindEditor(columnname, out editor);
-        }
-        public IDynamicEditorControl? FindEditor(string columnname)
-        {
-            return Editor.FindEditor(columnname);
+            if (Form.Editor.IsCustomLayout)
+                Title += "*";
         }
 
         private void Window_Closing(object sender, CancelEventArgs e)
         {
             if (DialogResult == true)
-                OnSaveItem?.Invoke(this, e);
+                Form.SaveItem(e);
         }
 
         #region Win32 API Stuff
@@ -512,14 +254,14 @@ namespace InABox.DynamicGrid
                 switch (wParam.ToInt32())
                 {
                     case _EditLayoutID:
-                        Editor.EditLayout();
+                        Form.EditLayout();
                         handled = true;
                         break;
                     case _ResetLayoutID:
                         if (MessageBox.Show(
                                 "WARNING: This will delete any customisations you have made!\n\nAre you sure you wish to reset this layout?",
                                 "Confirm Layout Reset", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
-                            Editor.ResetLayout();
+                            Form.ResetLayout();
                         handled = true;
                         break;
                 }

+ 195 - 174
InABox.DynamicGrid/DynamicEditorGrid.xaml.cs

@@ -156,10 +156,18 @@ namespace InABox.DynamicGrid
 
         private void DynamicEditorGrid_Loaded(object sender, RoutedEventArgs e)
         {
+            Reload();
+        }
+
+        public void Reload(bool refresh = false)
+        {
+            if (_loaded ^ refresh) return;
+
             ConfigureEditors();
 
             LoadEditorValues();
 
+            ClearPages();
             AddPages();
 
             DoReconfigureEditors();
@@ -180,6 +188,8 @@ namespace InABox.DynamicGrid
             _loaded = true;
         }
 
+        #region Configure Editors
+
         private void LoadLookupColumns(DynamicGridColumn column, Dictionary<string, string> othercolumns)
         {
             othercolumns.Clear();
@@ -220,6 +230,7 @@ namespace InABox.DynamicGrid
             if (popup.EditorDefinition is DataLookupEditor dataLookup)
                 LoadLookupColumns(column, dataLookup.OtherColumns);
 
+            popup.OnDefineFilter += (sender, type) => { return OnDefineFilter?.Invoke(sender, type); };
             popup.OnUpdateOtherEditor += Lookup_OnUpdateOtherEditor;
         }
 
@@ -303,6 +314,13 @@ namespace InABox.DynamicGrid
             document.Filter = editor.FileMask;
         }
 
+        private void Lookup_OnUpdateOtherEditor(string columnname, object value)
+        {
+            var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
+            if (editor != null)
+                CoreUtils.SetPropertyValue(editor, "Value", value);
+        }
+
         private void ConfigurePasswordEditor(PasswordEditorControl passwordEditorControl, DynamicGridColumn column, PasswordEditor passwordEditor)
         {
             passwordEditorControl.ViewButtonVisible = passwordEditor.ViewButtonVisible;
@@ -411,6 +429,75 @@ namespace InABox.DynamicGrid
                 }
         }
 
+        public virtual void DoReconfigureEditors()
+        {
+            ReconfigureEditors?.Invoke(this);
+        }
+
+        #endregion
+
+        #region Editor
+
+        private void EditorValueChanged(IDynamicEditorControl sender, Dictionary<string, object> values)
+        {
+            //Logger.Send(LogType.Information, "", string.Format("DynamicEditorGrid.EditorValueChanged({0})", values.Keys.Count));
+            var changededitors = new Dictionary<string, object?>();
+            foreach (var key in values.Keys)
+            {
+                var changedcolumns = OnEditorValueChanged?.Invoke(this, key, values[key]);
+                if (changedcolumns != null)
+                    foreach (var (change, value) in changedcolumns)
+                        if (editors.Any(x => x.ColumnName.Equals(change)) && !changededitors.ContainsKey(change) && !change.Equals(sender.ColumnName))
+                            changededitors[change] = value;
+            }
+
+            if (changededitors.Any())
+                LoadEditorValues(changededitors);
+            DoReconfigureEditors();
+        }
+
+        private void LoadEditorValues(Dictionary<string, object?>? changededitors = null)
+        {
+            var columnnames = changededitors != null ? changededitors.Keys.ToArray() : editors.Select(x => x.ColumnName).ToArray();
+            foreach (var columnname in columnnames)
+            {
+                var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
+
+                if (editor == null)
+                    continue;
+
+                var bLoaded = editor.Loaded;
+                editor.Loaded = false;
+                if (changededitors != null && changededitors.ContainsKey(columnname))
+                {
+                    CoreUtils.SetPropertyValue(editor, "Value", changededitors[columnname]);
+                }
+                else
+                {
+                    var curvalue = OnGetPropertyValue?.Invoke(this, columnname);
+
+                    try
+                    {
+                        CoreUtils.SetPropertyValue(editor, "Value", curvalue);
+                    }
+                    catch (Exception e)
+                    {
+                        MessageBox.Show($"Unable to set editor value for {columnname} -> {curvalue}: {CoreUtils.FormatException(e)}");
+                    }
+
+                    CoreUtils.SetPropertyValue(editor, "Changed", false);
+                }
+
+                editor.Loaded = bLoaded;
+
+                editor.OnEditorValueChanged += EditorValueChanged;
+            }
+        }
+
+        #endregion
+
+        #region Loading + Editing Layout
+
         private bool LoadLayout(string xaml)
         {
             if (!string.IsNullOrWhiteSpace(xaml))
@@ -639,7 +726,6 @@ namespace InABox.DynamicGrid
 
         }
 
-
         private Grid EnsureGrid(Dictionary<string, Grid> grids, string caption)
         {
             if (grids.ContainsKey(caption))
@@ -687,85 +773,6 @@ namespace InABox.DynamicGrid
             return result;
         }
 
-
-        //List<TabItem> configuredpages = new List<TabItem>();
-
-        private void Details_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            if (bChanging || Details?.SelectedItem == null || e.OriginalSource != Details)
-                return;
-
-
-            bChanging = true;
-            try
-            {
-                var tab = Details.SelectedItem as DynamicTabItem;
-                if(tab is not null)
-                {
-                    var page = tab.Content as IDynamicEditorPage;
-                    if (page is not null)
-                    {
-                        if (!page.Ready)
-                            using (new WaitCursor())
-                            {
-                                OnLoadPage?.Invoke(page);
-                            }
-                    }
-                    else
-                    {
-                        if (!_loaded || e.RemovedItems.Count == 0 || e.AddedItems.Count == 0 || e.AddedItems?[0] == e.RemovedItems?[0])
-                            return;
-
-
-                        //if (!configuredpages.Contains(tab))
-                        //{
-                        //    ConfigureEditors(eds);
-                        //    configuredpages.Add(tab);
-                        //}
-
-                        var selectedGrid = ((tab.Content as Border)?.Child as ScrollViewer)?.Content;
-
-                        var eds = editors
-                            .Where(x => x is BaseDynamicEditorControl control &&
-                                        control.Parent == selectedGrid)
-                            .Select(x => (BaseDynamicEditorControl)x);
-                        foreach (var ed in eds)
-                        {
-                            var editorvalue = ed.GetValue();
-                            var entityvalue = OnGetPropertyValue?.Invoke(this, ed.ColumnName);
-                            if (!Equals(editorvalue, entityvalue))
-                            {
-                                ed.Loaded = false;
-                                ed.SetValue(entityvalue);
-                                ed.Loaded = true;
-                            }
-                        }
-                    }
-
-                    OnSelectPage?.Invoke(tab, null);
-                }
-            }
-            finally
-            {
-                bChanging = false;
-            }
-        }
-
-        public void UnloadPages(bool saved)
-        {
-            if(Pages is not null)
-                foreach (var page in Pages)
-                    if (page.Ready)
-                        OnUnloadPage?.Invoke(page, saved);
-        }
-
-        private void Lookup_OnUpdateOtherEditor(string columnname, object value)
-        {
-            var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
-            if (editor != null)
-                CoreUtils.SetPropertyValue(editor, "Value", value);
-        }
-        
         private string FormatXML(string xml)
         {
             var result = "";
@@ -859,120 +866,79 @@ namespace InABox.DynamicGrid
             DoReconfigureEditors();
         }
 
-        private void LoadEditor(string columnname, object value)
-        {
-        }
+        #endregion
 
-        private void EditorValueChanged(IDynamicEditorControl sender, Dictionary<string, object> values)
-        {
-            //Logger.Send(LogType.Information, "", string.Format("DynamicEditorGrid.EditorValueChanged({0})", values.Keys.Count));
-            var changededitors = new Dictionary<string, object?>();
-            foreach (var key in values.Keys)
-            {
-                var changedcolumns = OnEditorValueChanged?.Invoke(this, key, values[key]);
-                if (changedcolumns != null)
-                    foreach (var (change, value) in changedcolumns)
-                        if (editors.Any(x => x.ColumnName.Equals(change)) && !changededitors.ContainsKey(change) && !change.Equals(sender.ColumnName))
-                            changededitors[change] = value;
-            }
+        #region Pages
 
-            if (changededitors.Any())
-                LoadEditorValues(changededitors);
-            DoReconfigureEditors();
-        }
+        //List<TabItem> configuredpages = new List<TabItem>();
 
-        private void LoadEditorValues(Dictionary<string, object?>? changededitors = null)
+        private void Details_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
-            var columnnames = changededitors != null ? changededitors.Keys.ToArray() : editors.Select(x => x.ColumnName).ToArray();
-            foreach (var columnname in columnnames)
-            {
-                var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
+            if (bChanging || Details?.SelectedItem == null || e.OriginalSource != Details)
+                return;
 
-                if (editor == null)
-                    continue;
 
-                var bLoaded = editor.Loaded;
-                editor.Loaded = false;
-                if (changededitors != null && changededitors.ContainsKey(columnname))
-                {
-                    CoreUtils.SetPropertyValue(editor, "Value", changededitors[columnname]);
-                }
-                else
+            bChanging = true;
+            try
+            {
+                var tab = Details.SelectedItem as DynamicTabItem;
+                if(tab is not null)
                 {
-                    var curvalue = OnGetPropertyValue?.Invoke(this, columnname);
-
-                    try
+                    var page = tab.Content as IDynamicEditorPage;
+                    if (page is not null)
                     {
-                        CoreUtils.SetPropertyValue(editor, "Value", curvalue);
+                        if (!page.Ready)
+                            using (new WaitCursor())
+                            {
+                                OnLoadPage?.Invoke(page);
+                            }
                     }
-                    catch (Exception e)
+                    else
                     {
-                        MessageBox.Show($"Unable to set editor value for {columnname} -> {curvalue}: {CoreUtils.FormatException(e)}");
-                    }
-
-                    CoreUtils.SetPropertyValue(editor, "Changed", false);
-                }
-
-                editor.Loaded = bLoaded;
-
-                editor.OnEditorValueChanged += EditorValueChanged;
-            }
-        }
+                        if (!_loaded || e.RemovedItems.Count == 0 || e.AddedItems.Count == 0 || e.AddedItems?[0] == e.RemovedItems?[0])
+                            return;
 
-        public virtual void DoReconfigureEditors()
-        {
-            ReconfigureEditors?.Invoke(this);
-        }
 
-        public void Load(string layoutname, DynamicEditorPages? pages = null)
-        {
-            _layoutname = layoutname;
-            Pages = pages;
+                        //if (!configuredpages.Contains(tab))
+                        //{
+                        //    ConfigureEditors(eds);
+                        //    configuredpages.Add(tab);
+                        //}
 
-            //Stopwatch sw = new Stopwatch();
-            //sw.Start();
-            _columns = OnCustomiseColumns?.Invoke(this, null) ?? new DynamicGridColumns();
-            //Logger.Send(LogType.Information, "DEG.Load", String.Format("Loaded Columns: {0}", sw.ElapsedMilliseconds));
-            //sw.Restart();
-            
-            var layout = new GlobalConfiguration<ScreenLayout>(_layoutname).Load();
-            //Logger.Send(LogType.Information, "DEG.Load", String.Format("Loaded Layout: {0}", sw.ElapsedMilliseconds));
-            //sw.Restart();
+                        var selectedGrid = ((tab.Content as Border)?.Child as ScrollViewer)?.Content;
 
-            if (!LoadLayout(layout.XAML))
-                CreateLayout();
-            
-            //Logger.Send(LogType.Information, "DEG.Load", String.Format("Created Layout: {0}", sw.ElapsedMilliseconds));
-            //sw.Restart();
-            
-        }
+                        var eds = editors
+                            .Where(x => x is BaseDynamicEditorControl control &&
+                                        control.Parent == selectedGrid)
+                            .Select(x => (BaseDynamicEditorControl)x);
+                        foreach (var ed in eds)
+                        {
+                            var editorvalue = ed.GetValue();
+                            var entityvalue = OnGetPropertyValue?.Invoke(this, ed.ColumnName);
+                            if (!Equals(editorvalue, entityvalue))
+                            {
+                                ed.Loaded = false;
+                                ed.SetValue(entityvalue);
+                                ed.Loaded = true;
+                            }
+                        }
+                    }
 
-        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
-        {
-            base.OnRenderSizeChanged(sizeInfo);
-            foreach (var columnname in editors.Select(x => x.ColumnName).ToArray())
-            {
-                var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
-                if(editor is not null)
-                {
-                    editor.Loaded = true;
+                    OnSelectPage?.Invoke(tab, null);
                 }
             }
-        }
-
-        public bool Unload()
-        {
-            ClearPages();
-
-            var bChanged = false;
-
-            foreach (var columnname in changes.Keys)
+            finally
             {
-                OnSetPropertyValue?.Invoke(this, columnname, changes[columnname]);
-                bChanged = true;
+                bChanging = false;
             }
+        }
 
-            return bChanged;
+        public void UnloadPages(bool saved)
+        {
+            if(Pages is not null)
+                foreach (var page in Pages)
+                    if (page.Ready)
+                        OnUnloadPage?.Invoke(page, saved);
         }
 
         private void AddPages()
@@ -1003,6 +969,7 @@ namespace InABox.DynamicGrid
                 var tab = pagemap[page];
                 tab.Content = null;
                 Details.Items.Remove(tab);
+                page.Ready = false;
             }
 
             pagemap.Clear();
@@ -1020,5 +987,59 @@ namespace InABox.DynamicGrid
             //    }
             //}
         }
+
+        public void Load(string layoutname, DynamicEditorPages? pages = null)
+        {
+            _layoutname = layoutname;
+            Pages = pages;
+
+            //Stopwatch sw = new Stopwatch();
+            //sw.Start();
+            _columns = OnCustomiseColumns?.Invoke(this, null) ?? new DynamicGridColumns();
+            //Logger.Send(LogType.Information, "DEG.Load", String.Format("Loaded Columns: {0}", sw.ElapsedMilliseconds));
+            //sw.Restart();
+            
+            var layout = new GlobalConfiguration<ScreenLayout>(_layoutname).Load();
+            //Logger.Send(LogType.Information, "DEG.Load", String.Format("Loaded Layout: {0}", sw.ElapsedMilliseconds));
+            //sw.Restart();
+
+            if (!LoadLayout(layout.XAML))
+                CreateLayout();
+
+            //Logger.Send(LogType.Information, "DEG.Load", String.Format("Created Layout: {0}", sw.ElapsedMilliseconds));
+            //sw.Restart();
+
+        }
+
+        public bool Unload()
+        {
+            ClearPages();
+
+            var bChanged = false;
+
+            foreach (var columnname in changes.Keys)
+            {
+                OnSetPropertyValue?.Invoke(this, columnname, changes[columnname]);
+                bChanged = true;
+            }
+
+            return bChanged;
+        }
+
+        #endregion
+
+        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+        {
+            base.OnRenderSizeChanged(sizeInfo);
+            foreach (var columnname in editors.Select(x => x.ColumnName).ToArray())
+            {
+                var editor = editors.FirstOrDefault(x => x.ColumnName.Equals(columnname));
+                if(editor is not null)
+                {
+                    editor.Loaded = true;
+                }
+            }
+        }
+
     }
 }

+ 3 - 3
InABox.DynamicGrid/DynamicEnclosedListGrid.cs

@@ -50,7 +50,7 @@ namespace InABox.DynamicGrid
             return int.MinValue;
         }
 
-        public void Load(object item, Func<Type, CoreTable> PageDataHandler)
+        public void Load(object item, Func<Type, CoreTable>? PageDataHandler)
         {
             Entity = (TOne)item;
             MasterList.Clear();
@@ -144,8 +144,8 @@ namespace InABox.DynamicGrid
                 Items.RemoveAt(row.Index);
         }
 
-        protected override void Reload(Filters<TMany> criteria, Columns<TMany> columns, ref SortOrder<TMany> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<TMany> criteria, Columns<TMany> columns, ref SortOrder<TMany>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             var results = new CoreTable();
             results.LoadColumns(typeof(TMany));

+ 1 - 1
InABox.DynamicGrid/DynamicEntityFormGrid.cs

@@ -83,7 +83,7 @@ namespace InABox.DynamicGrid
             base.Reload(criteria, columns, ref sort, action);
         }
 
-        private void BeforeSave(DynamicEditorForm editor, TForm[] items)
+        private void BeforeSave(IDynamicEditorForm editor, TForm[] items)
         {
             foreach(var item in items)
             {

+ 2 - 2
InABox.DynamicGrid/DynamicExportMappingGrid.cs

@@ -44,8 +44,8 @@ namespace InABox.DynamicGrid
             return Items[row.Index];
         }
 
-        protected override void Reload(Filters<ImportMapping> criteria, Columns<ImportMapping> columns, ref SortOrder<ImportMapping> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<ImportMapping> criteria, Columns<ImportMapping> columns, ref SortOrder<ImportMapping>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             var result = new CoreTable();
             result.LoadColumns(typeof(ImportMapping));

+ 36 - 29
InABox.DynamicGrid/DynamicGrid.cs

@@ -22,7 +22,6 @@ using InABox.Clients;
 using InABox.Core;
 using InABox.Scripting;
 using InABox.WPF;
-using NPOI.SS.Formula.Functions;
 using Syncfusion.Data;
 using Syncfusion.UI.Xaml.Grid;
 using Syncfusion.UI.Xaml.Grid.Cells;
@@ -336,7 +335,7 @@ namespace InABox.DynamicGrid
         private readonly Label Loading;
 
         protected Dictionary<string, CoreTable> Lookups = new();
-        private readonly Button MultiEdit;
+        //private readonly Button MultiEdit;
         private readonly Button Paste;
 
         private readonly Button Print;
@@ -491,9 +490,9 @@ namespace InABox.DynamicGrid
             Edit.Margin = new Thickness(0, 2, 2, 0);
             Edit.Click += Edit_Click;
 
-            MultiEdit = CreateButton(Properties.Resources.doc_xls.AsBitmapImage(Color.White));
+            /*MultiEdit = CreateButton(Properties.Resources.doc_xls.AsBitmapImage(Color.White));
             MultiEdit.Margin = new Thickness(0, 2, 2, 0);
-            MultiEdit.Click += MultiEdit_Click;
+            MultiEdit.Click += MultiEdit_Click;*/
 
             EditSpacer = new Label { Width = 5 };
 
@@ -534,7 +533,7 @@ namespace InABox.DynamicGrid
             Stack.Children.Add(Help);
             Stack.Children.Add(Add);
             Stack.Children.Add(Edit);
-            Stack.Children.Add(MultiEdit);
+            //Stack.Children.Add(MultiEdit);
             Stack.Children.Add(EditSpacer);
 
             Stack.Children.Add(Print);
@@ -636,8 +635,8 @@ namespace InABox.DynamicGrid
 
             Add.Visibility = Options.Contains(DynamicGridOption.AddRows) ? Visibility.Visible : Visibility.Collapsed;
             Edit.Visibility = Options.Contains(DynamicGridOption.EditRows) ? Visibility.Visible : Visibility.Collapsed;
-            MultiEdit.Visibility =
-                Visibility.Collapsed; // _Options.Contains(DynamicGridOptions.DirectEdit) ? Visibility.Visible : Visibility.Collapsed;
+            /*MultiEdit.Visibility =
+                Visibility.Collapsed; // _Options.Contains(DynamicGridOptions.DirectEdit) ? Visibility.Visible : Visibility.Collapsed;*/
             EditSpacer.Visibility = Options.Contains(DynamicGridOption.AddRows) || Options.Contains(DynamicGridOption.EditRows)
                 ? Visibility.Visible
                 : Visibility.Collapsed;
@@ -1232,8 +1231,8 @@ namespace InABox.DynamicGrid
             var result = new DynamicGridColumns();
 
             var cols = Options.Contains(DynamicGridOption.DirectEdit)
-                ? new Columns<T>().Default(ColumnType.IncludeForeignKeys)
-                : new Columns<T>().Default(ColumnType.IncludeLinked);
+                ? new Columns<T>().Default(ColumnType.IncludeForeignKeys, ColumnType.ExcludeID)
+                : new Columns<T>().Default(ColumnType.IncludeLinked, ColumnType.ExcludeID);
             result.AddRange(MasterColumns.Where(x => cols.Items.Any(c => c.Property.Equals(x.ColumnName)))
                 .OrderBy(x => CoreUtils.GetPropertySequence(typeof(T), x.ColumnName)));
             return result;
@@ -2244,7 +2243,7 @@ namespace InABox.DynamicGrid
             DoEdit();
         }
 
-        private void MultiEdit_Click(object sender, RoutedEventArgs e)
+        /*private void MultiEdit_Click(object sender, RoutedEventArgs e)
         {
             using (new WaitCursor())
             {
@@ -2273,9 +2272,9 @@ namespace InABox.DynamicGrid
                     }
                 );
             }
-        }
+        }*/
 
-        public override bool DirectEdit(CoreTable data)
+        /*public override bool DirectEdit(CoreTable data)
         {
             var window = new DynamicEditWindow<T>();
 
@@ -2350,7 +2349,7 @@ namespace InABox.DynamicGrid
             }
 
             return false;
-        }
+        }*/
 
         protected virtual void DoAdd()
         {
@@ -2392,22 +2391,19 @@ namespace InABox.DynamicGrid
             );
         }
 
-        protected virtual void CustomiseEditorForm(DynamicEditorForm form)
+        protected virtual void CustomiseEditorForm(IDynamicEditorForm form)
         {
         }
 
-        public override bool EditItems(T[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)
+        public override void InitialiseEditorForm(IDynamicEditorForm editor, T[] items, Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false)
         {
-            var cursor = new WaitCursor();
-
             var pages = items.Length == 1 ? LoadEditorPages(items.First()) : new DynamicEditorPages();
 
             var buttons = new DynamicEditorButtons();
             if (items.Length == 1)
                 LoadEditorButtons(items.First(), buttons);
 
-            var editor = new DynamicEditorForm(items.Any() ? items.First().GetType() : typeof(T), pages, buttons, PageDataHandler, PreloadPages);
-            editor.SetValue(Panel.ZIndexProperty, 999);
+            editor.Setup(items.Any() ? items.First().GetType() : typeof(T), pages, buttons, pageDataHandler, preloadPages);
 
             editor.OnCustomiseColumns += (o, c) =>
             {
@@ -2455,11 +2451,11 @@ namespace InABox.DynamicGrid
                     OnBeforeSave?.Invoke(editor, items);
 
                     if (items.Length == 1)
-                        editor.UnloadEditorPages(pages, items.First(), false);
+                        editor.UnloadEditorPages(false);
                     foreach (var item in items)
                         SaveItem(item);
                     if (items.Length == 1)
-                        editor.UnloadEditorPages(pages, items.First(), true);
+                        editor.UnloadEditorPages(true);
 
                     OnAfterSave?.Invoke(editor, items);
                 }
@@ -2469,12 +2465,23 @@ namespace InABox.DynamicGrid
                     e.Cancel = true;
                 }
             };
+
             CustomiseEditorForm(editor);
 
             editor.Items = items;
-
             AfterLoad(editor, items);
-            cursor.Dispose();
+        }
+
+        public override bool EditItems(T[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)
+        {
+            DynamicEditorForm editor;
+            using(var cursor = new WaitCursor())
+            {
+                editor = new DynamicEditorForm();
+                editor.SetValue(Panel.ZIndexProperty, 999);
+
+                InitialiseEditorForm(editor, items, PageDataHandler, PreloadPages);
+            }
 
             return editor.ShowDialog() == true;
         }
@@ -2519,7 +2526,7 @@ namespace InABox.DynamicGrid
 
 
 
-        protected virtual void AfterLoad(DynamicEditorForm editor, T[] items)
+        protected virtual void AfterLoad(IDynamicEditorForm editor, T[] items)
         {
         }
 
@@ -2527,7 +2534,7 @@ namespace InABox.DynamicGrid
         {
         }
 
-        protected virtual Dictionary<string, object?> EditorValueChanged(DynamicEditorForm editor, T[] items, string name, object value)
+        protected virtual Dictionary<string, object?> EditorValueChanged(IDynamicEditorForm editor, T[] items, string name, object value)
         {
             var result = DynamicGridUtils.UpdateEditorValue(items, name, value);
             if (OnEditorValueChanged != null)
@@ -2595,7 +2602,7 @@ namespace InABox.DynamicGrid
             return column.Editor ?? CoreUtils.GetProperty(item.GetType(), column.ColumnName).GetEditor();
         }
 
-        protected object DefineFilter(Type type, T[] items)
+        protected object? DefineFilter(Type type, T[] items)
         {
             return LookupFactory.DefineFilter(items, type);
         }
@@ -2741,7 +2748,7 @@ namespace InABox.DynamicGrid
             Process.Start(new ProcessStartInfo("https://prs-software.com.au/wiki/index.php/" + slug) { UseShellExecute = true });
         }
 
-        protected void ReloadForms<TTargetType, TTargetForm, TSourceForm>(DynamicEditorForm editor, TTargetType item,
+        protected void ReloadForms<TTargetType, TTargetForm, TSourceForm>(IDynamicEditorForm editor, TTargetType item,
             Expression<Func<TSourceForm, object?>> sourcekey, Guid sourceid)
             where TTargetType : Entity, new()
             where TTargetForm : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
@@ -3115,8 +3122,8 @@ namespace InABox.DynamicGrid
                 return true;
             if (Edit.Visibility != Visibility.Collapsed)
                 return true;
-            if (MultiEdit.Visibility != Visibility.Collapsed)
-                return true;
+            /*if (MultiEdit.Visibility != Visibility.Collapsed)
+                return true;*/
             if (Export.Visibility != Visibility.Collapsed)
                 return true;
             return false;

+ 1 - 1
InABox.DynamicGrid/DynamicGridFilterGrid.cs

@@ -43,7 +43,7 @@ namespace InABox.DynamicGrid
             return Filters[index];
         }
 
-        protected override void Reload(Filters<DynamicGridFilter> criteria, Columns<DynamicGridFilter> columns, ref SortOrder<DynamicGridFilter> sort, Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<DynamicGridFilter> criteria, Columns<DynamicGridFilter> columns, ref SortOrder<DynamicGridFilter>? sort, Action<CoreTable?, Exception?> action)
         {
             var result = new CoreTable();
             if (columns == null || !columns.Items.Any())

+ 9 - 5
InABox.DynamicGrid/DynamicGridStyle.cs

@@ -132,7 +132,7 @@ namespace InABox.DynamicGrid
     {
         private static bool initialized;
 
-        private static ScriptDocument helper;
+        private static ScriptDocument? helper;
 
         private static DynamicGridStyle defaultstyle;
 
@@ -171,11 +171,11 @@ namespace InABox.DynamicGrid
 
         protected abstract DynamicGridStyle CreateStyle();
 
-        private CoreRow GetRow(object item)
+        private CoreRow? GetRow(object item)
         {
             try
             {
-                var row = (item as DataRowBase).RowData as DataRowView;
+                var row = (item as DataRowBase)?.RowData as DataRowView;
                 if (row != null)
                 {
                     var index = row.Row.Table.Rows.IndexOf(row.Row);
@@ -213,7 +213,7 @@ namespace InABox.DynamicGrid
             initialized = true;
         }
 
-        public event OnGetDynamicGridStyle GetStyle;
+        public event OnGetDynamicGridStyle? GetStyle;
 
         private DynamicGridStyle ExecuteHelper(CoreRow row, DynamicGridStyle style)
         {
@@ -221,6 +221,10 @@ namespace InABox.DynamicGrid
 
             try
             {
+                if(helper is null)
+                {
+                    throw new Exception("Helper is null");
+                }
                 helper.SetValue("Row", row);
                 helper.SetValue("Background", style.Background);
                 helper.SetValue("Foreground", style.Foreground);
@@ -240,7 +244,7 @@ namespace InABox.DynamicGrid
             }
             catch (Exception e)
             {
-                //MessageBox.Show("Unable to Invoke Row Style Helper!\r\n\r\n" + e.Message);
+                Logger.Send(LogType.Information, "", "Unable to Invoke Row Style Helper: " + e.Message);
             }
 
             return result;

+ 7 - 7
InABox.DynamicGrid/DynamicImportGrid.cs

@@ -52,7 +52,7 @@ namespace InABox.DynamicGrid
             base.ShowHelp("Import_Data");
         }
 
-        private BitmapImage ImportImage(CoreRow arg)
+        private BitmapImage? ImportImage(CoreRow? arg)
         {
             return arg != null ? run : null;
         }
@@ -72,7 +72,7 @@ namespace InABox.DynamicGrid
             }
         }
 
-        private bool ImportAction(CoreRow arg)
+        private bool ImportAction(CoreRow? arg)
         {
             if (arg != null)
             {
@@ -80,7 +80,7 @@ namespace InABox.DynamicGrid
                     ImportFactory.Definitions.FirstOrDefault(x => x.Description.Equals(arg.Get<Importer, string>(c => c.ImporterDescription)));
                 if (definition != null)
                 {
-                    ScriptDocument helper = null;
+                    ScriptDocument? helper = null;
                     try
                     {
                         var script = arg.Get<Importer, string>(x => x.Script);
@@ -147,7 +147,7 @@ namespace InABox.DynamicGrid
                             var bOK = true;
                             if (helper != null)
                                 bOK = helper.Execute("Module", "AfterProcess", new[] { item, values });
-                            bOK = bOK && OnImportItem != null ? OnImportItem.Invoke(item) : true;
+                            bOK = !bOK || OnImportItem == null || OnImportItem.Invoke(item);
                             return bOK;
                         };
 
@@ -211,8 +211,8 @@ namespace InABox.DynamicGrid
             //throw new NotImplementedException();
         }
 
-        protected override void Reload(Filters<Importer> criteria, Columns<Importer> columns, ref SortOrder<Importer> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<Importer> criteria, Columns<Importer> columns, ref SortOrder<Importer>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             criteria.Add(new Filter<Importer>(x => x.EntityName).IsEqualTo(EntityType.EntityName()));
 
@@ -230,7 +230,7 @@ namespace InABox.DynamicGrid
             return result;
         }
 
-        public override bool EditItems(Importer[] items, Func<Type, CoreTable> PageDataHandler = null, bool PreloadPages = false)
+        public override bool EditItems(Importer[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)
         {
             if (items.Length != 1)
             {

+ 24 - 18
InABox.DynamicGrid/DynamicImportMappingGrid.cs

@@ -52,8 +52,8 @@ namespace InABox.DynamicGrid
             return Items[row.Index];
         }
 
-        protected override void Reload(Filters<ImportMapping> criteria, Columns<ImportMapping> columns, ref SortOrder<ImportMapping> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<ImportMapping> criteria, Columns<ImportMapping> columns, ref SortOrder<ImportMapping>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             Lookups.Clear();
             var result = new CoreTable();
@@ -71,29 +71,35 @@ namespace InABox.DynamicGrid
                 Items.Add(item);
         }
 
-        private FrameworkElement KeyToolTip(DynamicActionColumn column, CoreRow row)
+        private FrameworkElement? KeyToolTip(DynamicActionColumn column, CoreRow? row)
         {
             var result = "";
             if (row == null)
                 result = "This column is used to indicate which fields form the unique key imported records";
-            var key = row.Get<ImportMapping, bool>(x => x.Key);
-            result = key
-                ? "This field forms part (or all) of the unique key for imported records"
-                : "";
+            else
+            {
+                var key = row.Get<ImportMapping, bool>(x => x.Key);
+                result = key
+                    ? "This field forms part (or all) of the unique key for imported records"
+                    : "";
+            }
             return column.TextToolTip(result);
         }
 
-        private FrameworkElement LookupToolTip(DynamicActionColumn column, CoreRow row)
+        private FrameworkElement? LookupToolTip(DynamicActionColumn column, CoreRow? row)
         {
             var result = "";
             if (row == null)
                 result = "This column determines whether or not lookup values are automatically created as required, or cause the import to fail.";
-            var lookup = row.Get<ImportMapping, ImportLookupType>(x => x.Lookup);
-            result = lookup == ImportLookupType.None
-                ? ""
-                : lookup == ImportLookupType.Create
-                    ? "Create Lookup Values as required"
-                    : "Restrict Importing for invalid / non-existent Lookup Values";
+            else
+            {
+                var lookup = row.Get<ImportMapping, ImportLookupType>(x => x.Lookup);
+                result = lookup == ImportLookupType.None
+                    ? ""
+                    : lookup == ImportLookupType.Create
+                        ? "Create Lookup Values as required"
+                        : "Restrict Importing for invalid / non-existent Lookup Values";
+            }
             return column.TextToolTip(result);
         }
 
@@ -130,14 +136,14 @@ namespace InABox.DynamicGrid
             Refresh(false, true);
         }
 
-        private BitmapImage KeyImage(CoreRow arg)
+        private BitmapImage? KeyImage(CoreRow? arg)
         {
             if (arg == null)
                 return key;
             return arg.Get<ImportMapping, bool>(x => x.Key) ? key : null;
         }
 
-        private bool KeyAction(CoreRow arg)
+        private bool KeyAction(CoreRow? arg)
         {
             if (arg == null)
                 return false;
@@ -149,7 +155,7 @@ namespace InABox.DynamicGrid
             return true;
         }
 
-        private BitmapImage LookupImage(CoreRow arg)
+        private BitmapImage? LookupImage(CoreRow? arg)
         {
             if (arg == null)
                 return restrict;
@@ -169,7 +175,7 @@ namespace InABox.DynamicGrid
                     : null;
         }
 
-        private bool LookupAction(CoreRow arg)
+        private bool LookupAction(CoreRow? arg)
         {
             if (arg == null)
                 return false;

+ 2 - 2
InABox.DynamicGrid/DynamicManyToManyGrid.cs

@@ -285,8 +285,8 @@ namespace InABox.DynamicGrid
             Ready = true;
         }
 
-        protected override void Reload(Filters<TManyToMany> criteria, Columns<TManyToMany> columns, ref SortOrder<TManyToMany> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<TManyToMany> criteria, Columns<TManyToMany> columns, ref SortOrder<TManyToMany>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             var results = new CoreTable();
             results.LoadColumns(typeof(TManyToMany));

+ 2 - 2
InABox.DynamicGrid/DynamicMultiEditWindow.xaml

@@ -19,7 +19,7 @@
         </Style>
     </Window.Resources>
 
-    <Grid>
+    <!--Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="Auto" />
             <ColumnDefinition Width="*" />
@@ -63,5 +63,5 @@
                 Click="OK_Click" />
         <Button x:Name="Cancel" Grid.Row="1" Grid.Column="3" Width="80" Margin="0,0,2,2" Content="Cancel" Padding="5"
                 Click="Cancel_Click" />
-    </Grid>
+    </Grid-->
 </wpf:ThemableWindow>

+ 4 - 3
InABox.DynamicGrid/DynamicMultiEditWindow.xaml.cs

@@ -9,6 +9,7 @@ using Syncfusion.UI.Xaml.Grid;
 
 namespace InABox.DynamicGrid
 {
+    /*
     /// <summary>
     ///     Interaction logic for DynamicMultiEditWindow.xaml
     /// </summary>
@@ -87,9 +88,9 @@ namespace InABox.DynamicGrid
         {
             //EndEdit(e.RowColumnIndex.RowIndex - 1, e.RowColumnIndex.ColumnIndex, (e.SelectedItem as DataRowView)["ID"]);
         }
-    }
+    }*/
 
-    public class DynamicEditWindow<T> : DynamicMultiEditWindow where T : BaseObject, new()
+    /*public class DynamicEditWindow<T> : DynamicMultiEditWindow where T : BaseObject, new()
     {
         private CoreTable _data;
 
@@ -271,5 +272,5 @@ namespace InABox.DynamicGrid
             else if (row < table.Rows.Count)
                 table.Rows.RemoveAt(row);
         }
-    }
+    }*/
 }

+ 2 - 2
InABox.DynamicGrid/DynamicOneToManyGrid.cs

@@ -217,8 +217,8 @@ namespace InABox.DynamicGrid
             }
         }
 
-        protected override void Reload(Filters<TMany> criteria, Columns<TMany> columns, ref SortOrder<TMany> sort,
-            Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<TMany> criteria, Columns<TMany> columns, ref SortOrder<TMany>? sort,
+            Action<CoreTable?, Exception?> action)
         {
             //var exp = BaseObject.DefaultSortOrder<TMany>().Expression;
             //Items = Items.AsQueryable().SortBy(exp).To

+ 18 - 14
InABox.DynamicGrid/DynamicTreeView.cs

@@ -21,7 +21,7 @@ namespace InABox.DynamicGrid
     public class DynamicTreeNode : INotifyPropertyChanged
     {
 
-        private DynamicTreeNodes _owner = null;
+        private DynamicTreeNodes _owner;
         
         public ObservableCollection<DynamicTreeNode> Children => _owner.GetChilden(_id);
         
@@ -69,7 +69,7 @@ namespace InABox.DynamicGrid
             }
         }
 
-        public event PropertyChangedEventHandler PropertyChanged;
+        public event PropertyChangedEventHandler? PropertyChanged;
 
         public void RaisedOnPropertyChanged(string propertyName)
         {
@@ -79,6 +79,7 @@ namespace InABox.DynamicGrid
         public DynamicTreeNode(DynamicTreeNodes owner)
         {
             _owner = owner;
+            _description = "";
         }
 
         public DynamicTreeNode(DynamicTreeNodes owner, Guid id, Guid parent) : this(owner)
@@ -91,8 +92,8 @@ namespace InABox.DynamicGrid
     public class DynamicTreeNodes 
     {
 
-        private List<DynamicTreeNode> _nodes = null;
-        public ObservableCollection<DynamicTreeNode> Nodes => new ObservableCollection<DynamicTreeNode>(_nodes.Where(x=>x.Parent == Guid.Empty));
+        private List<DynamicTreeNode> _nodes;
+        public ObservableCollection<DynamicTreeNode> Nodes => new ObservableCollection<DynamicTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
         
         public DynamicTreeNodes()
         {
@@ -196,11 +197,6 @@ namespace InABox.DynamicGrid
             _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1D, GridUnitType.Star) });
             _grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1D, GridUnitType.Star) });
             _grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1D, GridUnitType.Auto) });
-
-            _menu = new ContextMenu();
-            var additem = new MenuItem() { Header = "Add Child Folder" };
-            additem.Click += (o, e) => { AddItem((_tree.SelectedItem as DynamicTreeNode).ID); };
-            _menu.Items.Add(additem);
             
             _tree = new SfTreeGrid();
             _tree.ChildPropertyName = "Children";
@@ -209,9 +205,14 @@ namespace InABox.DynamicGrid
             _tree.AutoExpandMode = AutoExpandMode.AllNodesExpanded;
             //_tree.NodeCollapsing += (o, e) => { e.Cancel = true; };
             _tree.HeaderRowHeight = 0D;
-            _tree.SelectionChanged += (o,e) => OnSelectItem?.Invoke(_tree.SelectedItem as DynamicTreeNode);
+            _tree.SelectionChanged += (o,e) => OnSelectItem?.Invoke((_tree.SelectedItem as DynamicTreeNode)!);
             _tree.AllowSelectionOnExpanderClick = false;
 
+            _menu = new ContextMenu();
+            var additem = new MenuItem() { Header = "Add Child Folder" };
+            additem.Click += (o, e) => { AddItem((_tree.SelectedItem as DynamicTreeNode)!.ID); };
+            _menu.Items.Add(additem);
+
             _tree.ContextMenuOpening += _tree_ContextMenuOpening;
             _tree.ContextMenu = _menu;
             _tree.Background = new SolidColorBrush(Colors.DimGray);
@@ -471,11 +472,14 @@ namespace InABox.DynamicGrid
                         MessageBox.Show(String.Format("Error: {0}", exception.Message));
                     });
                 }
-                Data = table;
-                Dispatcher.Invoke(() =>
+                else if(table is not null)
                 {
-                    AfterRefresh();
-                });
+                    Data = table;
+                    Dispatcher.Invoke(() =>
+                    {
+                        AfterRefresh();
+                    });
+                }
             });
         }
 

+ 77 - 1
InABox.DynamicGrid/Editors/DocumentConfirm.xaml.cs

@@ -1,5 +1,9 @@
-using InABox.Wpf;
+using InABox.Core;
+using InABox.Wpf;
+using NPOI.HPSF;
 using System;
+using System.IO;
+using System.Reactive;
 using System.Windows;
 
 namespace InABox.DynamicGrid
@@ -60,5 +64,77 @@ namespace InABox.DynamicGrid
             DialogResult = false;
             Close();
         }
+
+        public static Document? CheckDocument(Document newDocument, DocumentEditorControl.FindDocumentEvent? findDocument, out bool shouldSave)
+        {
+            var existing = findDocument?.Invoke(newDocument.FileName);
+            if (existing != null)
+            {
+                if ((existing.TimeStamp == DateTime.MinValue || existing.TimeStamp.ToString("yyyy-MM-ddThh:mm.ss.fff")
+                        .Equals(newDocument.TimeStamp.ToString("yyyy-MM-ddThh:mm.ss.fff"))) && existing.CRC.Equals(newDocument.CRC))
+                {
+                    if (existing.TimeStamp == DateTime.MinValue)
+                    {
+                        existing.TimeStamp = newDocument.TimeStamp;
+                        shouldSave = true;
+                    }
+                    else
+                    {
+                        shouldSave = false;
+                    }
+
+                    return existing;
+                }
+                else
+                {
+                    var confirm = new DocumentConfirm
+                    {
+                        FileName = newDocument.FileName,
+                        LocalSize = newDocument.Data.Length,
+                        RemoteSize = existing.Data.Length,
+                        LocalTimeStamp = newDocument.TimeStamp,
+                        RemoteTimeStamp = existing.TimeStamp
+                    };
+                    if (confirm.ShowDialog() == true)
+                    {
+                        if (confirm.Result == DocumentAction.Replace)
+                        {
+                            existing.Data = newDocument.Data;
+                            existing.TimeStamp = newDocument.TimeStamp;
+                            existing.CRC = newDocument.CRC;
+                            shouldSave = true;
+                            return existing;
+                        }
+                        else if (confirm.Result == DocumentAction.UseExisting)
+                        {
+                            shouldSave = false;
+                            return existing;
+                        }
+                        else if (confirm.Result == DocumentAction.MakeCopy)
+                        {
+                            var basefilename = Path.GetFileNameWithoutExtension(newDocument.FileName);
+                            var ext = Path.GetExtension(newDocument.FileName);
+                            var i = 0;
+                            while (existing is not null)
+                            {
+                                i++;
+                                newDocument.FileName = Path.ChangeExtension(string.Format("{0} ({1})", basefilename, i), ext);
+                                existing = findDocument?.Invoke(newDocument.FileName);
+                            }
+
+                            shouldSave = true;
+                            return newDocument;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                shouldSave = true;
+                return newDocument;
+            }
+            shouldSave = false;
+            return null;
+        }
     }
 }

+ 86 - 70
InABox.DynamicGrid/Editors/DocumentEditorControl.cs

@@ -126,79 +126,95 @@ namespace InABox.DynamicGrid
                 var data = File.ReadAllBytes(dlg.FileName);
                 var crc = CoreUtils.CalculateCRC(data);
 
-                var existing = OnFindDocument?.Invoke(filename);
-                if (existing != null)
-                {
-                    if ((existing.TimeStamp == DateTime.MinValue || existing.TimeStamp.ToString("yyyy-MM-ddThh:mm.ss.fff")
-                            .Equals(timestamp.ToString("yyyy-MM-ddThh:mm.ss.fff"))) && existing.CRC.Equals(crc))
-                    {
-                        if (existing.TimeStamp == DateTime.MinValue)
-                        {
-                            existing.TimeStamp = timestamp;
-                            OnSaveDocument?.Invoke(existing);
-                        }
+                //var existing = OnFindDocument?.Invoke(filename);
+                //if (existing != null)
+                //{
+                //    if ((existing.TimeStamp == DateTime.MinValue || existing.TimeStamp.ToString("yyyy-MM-ddThh:mm.ss.fff")
+                //            .Equals(timestamp.ToString("yyyy-MM-ddThh:mm.ss.fff"))) && existing.CRC.Equals(crc))
+                //    {
+                //        if (existing.TimeStamp == DateTime.MinValue)
+                //        {
+                //            existing.TimeStamp = timestamp;
+                //            OnSaveDocument?.Invoke(existing);
+                //        }
 
-                        _document = existing;
-                    }
-                    else
-                    {
-                        var confirm = new DocumentConfirm
-                        {
-                            FileName = filename,
-                            LocalSize = data.Length,
-                            RemoteSize = existing.Data.Length,
-                            LocalTimeStamp = timestamp,
-                            RemoteTimeStamp = existing.TimeStamp
-                        };
-                        if (confirm.ShowDialog() == true)
-                        {
-                            if (confirm.Result == DocumentAction.Replace)
-                            {
-                                existing.Data = data;
-                                existing.TimeStamp = timestamp;
-                                existing.CRC = crc;
-                                OnSaveDocument?.Invoke(existing);
-                                _document = existing;
-                            }
-                            else if (confirm.Result == DocumentAction.UseExisting)
-                            {
-                                _document = existing;
-                            }
-                            else if (confirm.Result == DocumentAction.MakeCopy)
-                            {
-                                var basefilename = Path.GetFileNameWithoutExtension(filename);
-                                var ext = Path.GetExtension(filename);
-                                var i = 0;
-                                while (existing is not null)
-                                {
-                                    i++;
-                                    filename = Path.ChangeExtension(string.Format("{0} ({1})", basefilename, i), ext);
-                                    existing = OnFindDocument?.Invoke(filename);
-                                }
-
-                                var document = new Document
-                                {
-                                    FileName = filename,
-                                    Data = data,
-                                    TimeStamp = timestamp,
-                                    CRC = crc
-                                };
-                                OnSaveDocument?.Invoke(document);
-                                _document = document;
-                            }
-                        }
-                    }
-                }
-                else
+                //        _document = existing;
+                //    }
+                //    else
+                //    {
+                //        var confirm = new DocumentConfirm
+                //        {
+                //            FileName = filename,
+                //            LocalSize = data.Length,
+                //            RemoteSize = existing.Data.Length,
+                //            LocalTimeStamp = timestamp,
+                //            RemoteTimeStamp = existing.TimeStamp
+                //        };
+                //        if (confirm.ShowDialog() == true)
+                //        {
+                //            if (confirm.Result == DocumentAction.Replace)
+                //            {
+                //                existing.Data = data;
+                //                existing.TimeStamp = timestamp;
+                //                existing.CRC = crc;
+                //                OnSaveDocument?.Invoke(existing);
+                //                _document = existing;
+                //            }
+                //            else if (confirm.Result == DocumentAction.UseExisting)
+                //            {
+                //                _document = existing;
+                //            }
+                //            else if (confirm.Result == DocumentAction.MakeCopy)
+                //            {
+                //                var basefilename = Path.GetFileNameWithoutExtension(filename);
+                //                var ext = Path.GetExtension(filename);
+                //                var i = 0;
+                //                while (existing is not null)
+                //                {
+                //                    i++;
+                //                    filename = Path.ChangeExtension(string.Format("{0} ({1})", basefilename, i), ext);
+                //                    existing = OnFindDocument?.Invoke(filename);
+                //                }
+
+                //                var document = new Document
+                //                {
+                //                    FileName = filename,
+                //                    Data = data,
+                //                    TimeStamp = timestamp,
+                //                    CRC = crc
+                //                };
+                //                OnSaveDocument?.Invoke(document);
+                //                _document = document;
+                //            }
+                //        }
+                //    }
+                //}
+                //else
+                //{
+                //    _document = new Document
+                //    {
+                //        FileName = filename,
+                //        Data = data,
+                //        TimeStamp = timestamp,
+                //        CRC = crc
+                //    };
+                //    OnSaveDocument?.Invoke(_document);
+                //}
+
+                var newDocument = DocumentConfirm.CheckDocument(new Document
                 {
-                    _document = new Document
+                    FileName = filename,
+                    TimeStamp = timestamp,
+                    Data = data,
+                    CRC = crc
+                }, OnFindDocument, out var shouldSave);
+                if(newDocument != null)
+                {
+                    _document = newDocument;
+                    if (shouldSave)
                     {
-                        FileName = filename,
-                        Data = data,
-                        TimeStamp = timestamp,
-                        CRC = crc
-                    };
-                    OnSaveDocument?.Invoke(_document);
+                        OnSaveDocument?.Invoke(newDocument);
+                    }
                 }
 
                 Editor.Text = _document.FileName;

+ 3 - 3
InABox.DynamicGrid/Editors/JsonEditorControl.cs

@@ -27,7 +27,7 @@ namespace InABox.DynamicGrid
             return row.ToObject<T>();
         }
 
-        protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T> sort, Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T>? sort, Action<CoreTable?, Exception?> action)
         {
         }
 
@@ -102,7 +102,7 @@ namespace InABox.DynamicGrid
             data.LoadColumns(type);
             data.LoadRows(array);
 
-            var gridtype = typeof(DynamicJsonGrid<>).MakeGenericType(type);
+            /*var gridtype = typeof(DynamicJsonGrid<>).MakeGenericType(type);
             var grid = Activator.CreateInstance(gridtype) as IDynamicGrid;
             grid.Data = data;
             if (grid.DirectEdit(data))
@@ -110,7 +110,7 @@ namespace InABox.DynamicGrid
                 var saved = data.Rows.Select(x => x.ToObject(type)).ToArray();
                 _value = Serialization.Serialize(saved, true);
                 CheckChanged();
-            }
+            }*/
         }
 
         protected override string RetrieveValue()

+ 7 - 6
InABox.DynamicGrid/Editors/MultiLookupEditorControl.cs

@@ -12,12 +12,10 @@ using Syncfusion.Data.Extensions;
 
 namespace InABox.DynamicGrid
 {
-    public class MultiLookupEditorControl : DynamicEditorControl<object>, ILookupEditorControl
+    public class MultiLookupEditorControl : DynamicEditorControl<object?>, ILookupEditorControl
     {
         private ComboBoxAdv Editor;
 
-        private CoreTable LookupTable;
-
         public MultiLookupEditorControl()
         {
             OtherColumns = new Dictionary<string, string>();
@@ -58,7 +56,6 @@ namespace InABox.DynamicGrid
             var value = RetrieveValue();
 
             //Lookups = lookups;
-            LookupTable = values;
 
             Lookups.Clear();
 
@@ -71,8 +68,12 @@ namespace InABox.DynamicGrid
 
             foreach (var row in values.Rows)
             {
-                Items.Add(new(row[keycol], $"{row["Display"]}"));
-                Lookups[row[keycol]] = string.Format("{0}", row["Display"]);
+                var key = row[keycol];
+                Items.Add(new(key, $"{row["Display"]}"));
+                if (key != null)
+                {
+                    Lookups[key] = string.Format("{0}", row["Display"]);
+                }
             }
             Editor.ItemsSource = Items;
             Editor.DisplayMemberPath = "Item2";

+ 17 - 11
InABox.DynamicGrid/Editors/PopupEditorControl.cs

@@ -9,11 +9,9 @@ using InABox.Core;
 
 namespace InABox.DynamicGrid
 {
-    internal class PopupEditorControl : DynamicEditorControl<Guid>
+    public class PopupEditorControl : DynamicEditorControl<Guid>, IPopupEditorControl
     {
-        private string _description = "";
-
-        private Type _type;
+        private Type? _type;
         private Guid _value = Guid.Empty;
 
         private TextBox Editor;
@@ -26,6 +24,8 @@ namespace InABox.DynamicGrid
 
         public Dictionary<string, string> OtherColumns { get; }
 
+        public event OnDefineFilter? OnDefineFilter;
+
         public event OnUpdateOtherEditorHandler OnUpdateOtherEditor;
 
         protected override FrameworkElement CreateEditor()
@@ -87,6 +87,7 @@ namespace InABox.DynamicGrid
             {
                 var list = new PopupList(_type, _value, OtherColumns.Keys.ToArray());
 
+                list.OnDefineFilter += (o, e) => { return OnDefineFilter?.Invoke(o, e); };
                 //list.OnDefineFilter += (o, t) => { return OnDefineFilter?.Invoke(o, t); };
 
                 if (list.ShowDialog() == true)
@@ -102,12 +103,12 @@ namespace InABox.DynamicGrid
                     //fieldmap[OtherColumns[key]] = list.OtherValues[othercolumn]
                     CheckChanged();
 
-                    var display = new List<string>();
+                    var display = new List<string?>();
                     //var columns = Entity.DefaultLookupColumns(_type) as IColumns;
                     var columns = LookupFactory.DefineColumns(_type);
                     foreach (var column in columns.ColumnNames())
                         if (list.OtherValues.ContainsKey(column))
-                            display.Add(list.OtherValues[column].ToString());
+                            display.Add(list.OtherValues[column]?.ToString());
                     Editor.Text = string.Join(" / ", display.Where(x => x != null && !string.IsNullOrWhiteSpace(x.ToString())));
                 }
             }
@@ -143,17 +144,22 @@ namespace InABox.DynamicGrid
         protected override void UpdateValue(Guid value)
         {
             _value = value;
+            if (_type is null)
+            {
+                Logger.Send(LogType.Error, "", "PopupEditorControl.UpdateValue(): _type is null!");
+                return;
+            }
 
             var client = ClientFactory.CreateClient(_type);
             var columns = LookupFactory.DefineColumns(_type);
             var sort = LookupFactory.DefineSort(_type);
-            var filter = Activator.CreateInstance(typeof(Filter<>).MakeGenericType(_type));
-            CoreUtils.SetPropertyValue(filter, "Expression", CoreUtils.GetMemberExpression(_type, "ID"));
-            CoreUtils.SetPropertyValue(filter, "Operator", Operator.IsEqualTo);
-            CoreUtils.SetPropertyValue(filter, "Value", value);
+            var filter = Filter.Create(_type);
+            filter.Expression = CoreUtils.GetMemberExpression(_type, "ID");
+            filter.Operator = Operator.IsEqualTo;
+            filter.Value = value;
 
             var lookup = client.Query(filter, columns, sort);
-            var display = new List<object>();
+            var display = new List<object?>();
             if (lookup.Rows.Any())
                 foreach (var col in lookup.Columns.Where(x => !x.ColumnName.Equals("ID")))
                     display.Add(lookup.Rows.First()[col.ColumnName]);

+ 2 - 4
InABox.DynamicGrid/Editors/RichTextEditor.xaml.cs

@@ -31,7 +31,6 @@ namespace InABox.DynamicGrid
                 new PropertyMetadata(default(Color), OnBackgroundPropertyChanged));
 
         private bool bdirty;
-        private bool bLoaded;
         private bool bReady;
         private string curvalue = "";
 
@@ -88,7 +87,7 @@ namespace InABox.DynamicGrid
             set => Load(value);
         }
 
-        public RichTextEditorChanged OnChanged { get; set; }
+        public RichTextEditorChanged? OnChanged { get; set; }
 
         public Color Color
         {
@@ -113,7 +112,6 @@ namespace InABox.DynamicGrid
 
             curvalue = content;
             bdirty = false;
-            bLoaded = true;
         }
 
         public string Save()
@@ -144,7 +142,7 @@ namespace InABox.DynamicGrid
 
         private static void OnBackgroundPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
-            var source = d as RichTextEditor;
+            if (d is not RichTextEditor source) return;
             source.SetColor((Color)e.NewValue);
         }
 

+ 34 - 0
InABox.DynamicGrid/EmbeddedDynamicEditorForm.xaml

@@ -0,0 +1,34 @@
+<UserControl x:Class="InABox.DynamicGrid.EmbeddedDynamicEditorForm"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:InABox.DynamicGrid"
+             mc:Ignorable="d" 
+             d:DesignHeight="450" d:DesignWidth="800">
+
+    <Grid x:Name="LayoutGrid">
+
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="*" />
+            <ColumnDefinition Width="80" />
+            <ColumnDefinition Width="80" />
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*" />
+            <RowDefinition Height="40" />
+        </Grid.RowDefinitions>
+
+        <local:DynamicEditorGrid x:Name="Editor"
+                                 Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="5,5,5,0"
+                                 HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
+
+        <StackPanel Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" Orientation="Horizontal"
+                    x:Name="Buttons">
+            <!--<Button x:Name="ShowHelp" Content="Help" HorizontalAlignment="Left" Width="80" Grid.Row="2" Grid.Column="0" Margin="5,5,5,5" Click="ShowHelp_Click"/>-->
+        </StackPanel>
+        <Button x:Name="OKButton" Content="OK" Grid.Row="1" Grid.Column="1" Margin="0,5,5,5" Click="OKButton_Click" />
+        <Button x:Name="CancelButton" Content="Cancel" Grid.Row="1" Grid.Column="2" Margin="0,5,5,5"
+                Click="CancelButton_Click" />
+    </Grid>
+</UserControl>

+ 414 - 0
InABox.DynamicGrid/EmbeddedDynamicEditorForm.xaml.cs

@@ -0,0 +1,414 @@
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Document = InABox.Core.Document;
+
+namespace InABox.DynamicGrid
+{
+    /// <summary>
+    /// Interaction logic for EmbeddedDynamicEditorForm.xaml
+    /// </summary>
+    public partial class EmbeddedDynamicEditorForm : UserControl, IDynamicEditorForm
+    {
+
+        public delegate void OKEvent();
+
+        public delegate void CancelEvent();
+
+        public DynamicEditorPages? Pages { get; private set; }
+
+        private BaseObject[] _items;
+
+        public BaseObject[] Items
+        {
+            get => _items;
+            set
+            {
+                _items = value;
+                UtilityViewModel.Slug = Items != null ? Items.Any() ? Items.First().GetType().EntityName().Split('.').Last() : "" : "";
+                Editor.Load(_items.First().GetType().EntityName(), Pages);
+
+                Editor.Reload(true);
+            }
+        }
+
+        public bool ReadOnly { get; set; }
+
+        #region Events
+
+
+        public event OnValidateData? OnValidateData;
+
+        public event OnCustomiseColumns? OnCustomiseColumns;
+        public event OnDefineFilter? OnDefineFilter;
+
+        public event OnDefineLookup? OnDefineLookups;
+        public event OnLookupsDefined? OnLookupsDefined;
+
+        public event DefineEditorEventHandler? OnDefineEditor;
+        public event OnFormCustomiseEditor? OnFormCustomiseEditor;
+        public event OnReconfigureEditors? ReconfigureEditors;
+
+        public event EditorValueChangedHandler? OnEditorValueChanged;
+
+        public event IDynamicEditorForm.GetDocumentEvent? OnGetDocument;
+        public event IDynamicEditorForm.FindDocumentEvent? OnFindDocument;
+        public event IDynamicEditorForm.SaveDocumentEvent? OnSaveDocument;
+
+        public event OnSelectPage? OnSelectPage;
+
+        public event DynamicGridSaveEvent? OnSaveItem;
+
+        public event DynamicEditorGrid.EditorCreatedHandler? OnEditorCreated;
+
+        public event OKEvent? OnOK;
+        public event CancelEvent? OnCancel;
+
+        #endregion
+
+        public EmbeddedDynamicEditorForm()
+        {
+            InitializeComponent();
+        }
+        public EmbeddedDynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+            Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false): this()
+        {
+            Setup(type, pages, buttons, PageDataHandler, PreloadPages);
+        }
+
+        public void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+            Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)
+        {
+            ReadOnly = false;
+
+            //this.Loaded += new RoutedEventHandler(ConfigureSystemMenu);
+
+            Editor.UnderlyingType = type;
+
+            Editor.OnCustomiseColumns += Editor_OnCustomiseColumns;
+
+            Editor.OnDefineFilter += (sender, t) => OnDefineFilter?.Invoke(sender, t);
+
+            Editor.OnEditorCreated += Editor_OnEditorCreated;
+
+            Editor.OnLoadPage += page => { page.Load(Items.First(), PageDataHandler); };
+
+            Editor.OnSelectPage += (tab, items) => { OnSelectPage?.Invoke(tab, items); };
+
+            Editor.PreloadPages = PreloadPages;
+
+            Editor.OnUnloadPage += (page, saved) =>
+            {
+                if (!saved)
+                    page.BeforeSave(Items.First());
+                else
+                    page.AfterSave(Items.First());
+            };
+
+            //Editor.OnGetPropertyInfo += (o, c) => { return CoreUtils.GetProperty(_item.GetType(), c); };
+
+            Editor.ReconfigureEditors += g => { ReconfigureEditors?.Invoke(g); };
+
+            Editor.OnGetEditor += c =>
+            {
+                if (_items != null && _items.Any())
+                {
+                    var property = DatabaseSchema.Property(type, c.ColumnName);
+                    if (property == null) return new NullEditor();
+
+                    if (property.Editor is NullEditor)
+                        return property.Editor;
+
+                    BaseEditor editor;
+                    if (property is CustomProperty)
+                    {
+                        editor = property.Editor.CloneEditor();
+                    }
+                    else
+                    {
+                        editor = OnDefineEditor?.Invoke(_items[0], c) ?? c.Editor.CloneEditor();
+                        var propEditor = property.Editor;
+                        editor.Page = propEditor.Page;
+                        editor.Caption = propEditor.Caption;
+                    }
+
+
+                    //defaultEditor.EditorSequence
+
+                    //EditorUtils.GetPropertyEditor(type, property, defaultEditor);
+
+                    /*BaseEditor editor = new NullEditor();
+                    var caption = "";
+                    var page = "";
+
+                    try
+                    {
+                        var comps = c.ColumnName.Split('.');
+                        for (var i = 0; i < comps.Length; i++)
+                        {
+                            var column = string.Join(".", comps.Take(i + 1));
+                            var prop = CoreUtils.GetProperty(type, column);
+
+                            if (column.Equals(c.ColumnName))
+                            {
+                                if (OnDefineEditor != null)
+                                    editor = OnDefineEditor(_items[0], c);
+                                else
+                                    editor = c.Editor != null ? c.Editor : new NullEditor();
+                            }
+                            else
+                            {
+                                var pedit = prop.GetEditor();
+                                if (pedit is NullEditor)
+                                    return pedit;
+                            }
+
+                            editor = editor == null ? new NullEditor() : editor.Clone() as BaseEditor;
+
+                            var capattr = prop.GetCustomAttribute<Caption>();
+                            var subcap = capattr != null ? capattr.Text : comps[i];
+                            var path = capattr != null ? capattr.IncludePath : true;
+                            if (!string.IsNullOrWhiteSpace(subcap))
+                                caption = string.IsNullOrWhiteSpace(caption) || path == false ? subcap : string.Format("{0} {1}", caption, subcap);
+
+                            if (string.IsNullOrWhiteSpace(page))
+                            {
+                                var pageattr = prop.GetCustomAttribute<EditorSequence>();
+                                if (pageattr != null)
+                                    page = pageattr.Page;
+                            }
+                        }
+
+                        editor.Caption = caption;
+                        editor.Page = page;
+                    }
+                    catch (Exception e)
+                    {
+                        var dmprop = DatabaseSchema.Property(_items[0].GetType(), c.ColumnName);
+                        if (dmprop is CustomProperty)
+                        {
+                            editor = dmprop.Editor.Clone() as BaseEditor;
+                            editor.Caption = dmprop.Caption;
+                            editor.Page = string.IsNullOrWhiteSpace(dmprop.Page) ? "Custom Fields" : dmprop.Page;
+                        }
+                    }*/
+
+                    if (ReadOnly && editor.Editable.Equals(Editable.Enabled))
+                        editor.Editable = Editable.Disabled;
+                    return editor;
+                }
+
+                return new NullEditor();
+            };
+
+            Editor.OnGridCustomiseEditor += (sender, column, editor) => OnFormCustomiseEditor?.Invoke(this, Items, column, editor);
+
+            Editor.OnGetSequence += c => CoreUtils.GetPropertySequence(_items.First().GetType(), c.ColumnName);
+
+            Editor.OnGetPropertyValue += (o, c) =>
+            {
+                if (!_items.Any())
+                    return null;
+
+                object? result;
+                try
+                {
+                    result = CoreUtils.GetPropertyValue(_items.First(), c);
+                }
+                catch
+                {
+                    result = _items.First().UserProperties.ContainsKey(c) ? _items.First().UserProperties[c] : null;
+                }
+
+                if (result == null)
+                    return null;
+
+                foreach (var _item in _items)
+                {
+                    object? curvalue;
+                    try
+                    {
+                        curvalue = CoreUtils.GetPropertyValue(_item, c);
+                    }
+                    catch
+                    {
+                        curvalue = _item.UserProperties.ContainsKey(c) ? _item.UserProperties[c] : null;
+                    }
+
+                    if (curvalue == null)
+                        return null;
+
+                    if (!curvalue.Equals(result))
+                        return null;
+                }
+
+                return result;
+            };
+
+            Editor.OnSetPropertyValue += (o, c, v) =>
+            {
+                foreach (var _item in _items)
+                    if (_item.UserProperties.ContainsKey(c))
+                        _item.UserProperties[c] = v;
+                    else
+                        CoreUtils.SetPropertyValue(_item, c, v);
+            };
+
+            Editor.OnEditorValueChanged += EditorValueChanged;
+
+            Editor.OnDefineLookups += sender => { OnDefineLookups?.Invoke(sender); };
+            Editor.OnLookupsDefined += sender => { OnLookupsDefined?.Invoke(sender); };
+
+            Editor.OnGetDocument += id => { return OnGetDocument?.Invoke(id); };
+            Editor.OnSaveDocument += doc => { OnSaveDocument?.Invoke(doc); };
+            Editor.OnFindDocument += file => { return OnFindDocument?.Invoke(file); };
+
+            Editor.GetItems += () => _items;
+
+            Pages = pages;
+
+            if (Pages == null || Pages.Count == 0)
+                Editor.Margin = new Thickness(5, 5, 5, 0);
+
+            if (buttons != null)
+                foreach (var button in buttons)
+                {
+                    var btn = new Button();
+                    UpdateButton(btn, button.Image, button.Name);
+                    btn.Tag = button;
+                    btn.Margin = new Thickness(5, 5, 0, 5);
+                    btn.Padding = new Thickness(5, 0, 5, 0);
+                    btn.Click += Btn_Click;
+                    Buttons.Children.Add(btn);
+                    button.Button = btn;
+                    button.Form = this;
+                }
+        }
+
+        public void UnloadEditorPages(bool saved)
+        {
+            Editor.UnloadPages(saved);
+        }
+
+        protected void UpdateButton(Button button, BitmapImage? image, string text)
+        {
+            var stackPnl = new StackPanel();
+            stackPnl.Orientation = Orientation.Horizontal;
+            //stackPnl.Margin = new Thickness(2);
+
+            if (image != null)
+            {
+                var img = new Image();
+                img.Source = image;
+                img.Margin = new Thickness(2);
+                stackPnl.Children.Add(img);
+            }
+
+            if (!string.IsNullOrEmpty(text))
+            {
+                var lbl = new Label();
+                lbl.Content = text;
+                lbl.VerticalAlignment = VerticalAlignment.Stretch;
+                lbl.VerticalContentAlignment = VerticalAlignment.Center;
+                lbl.Margin = new Thickness(2, 0, 5, 0);
+                stackPnl.Children.Add(lbl);
+            }
+
+            button.Content = stackPnl;
+        }
+
+        private Dictionary<string, object?> EditorValueChanged(object sender, string name, object value)
+        {
+            if (OnEditorValueChanged != null)
+                return OnEditorValueChanged(sender, name, value);
+            return DynamicGridUtils.UpdateEditorValue(_items, name, value);
+        }
+
+        private void Editor_OnEditorCreated(object sender, double height, double width)
+        {
+            OnEditorCreated?.Invoke(sender, height, width);
+
+            Editor.VerticalAlignment = VerticalAlignment.Stretch;
+            Editor.HorizontalAlignment = HorizontalAlignment.Stretch;
+
+            OKButton.IsEnabled = !ReadOnly;
+        }
+
+        private DynamicGridColumns Editor_OnCustomiseColumns(object sender, DynamicGridColumns? source)
+        {
+            var columns = new DynamicGridColumns();
+            if (_items != null && _items.Any())
+                columns.ExtractColumns(_items.First().GetType(), "");
+            if (OnCustomiseColumns != null)
+                columns = OnCustomiseColumns.Invoke(this, columns);
+            return columns;
+        }
+
+        private void Btn_Click(object sender, RoutedEventArgs e)
+        {
+            var button = (Button)sender;
+            var deb = (DynamicEditorButton)button.Tag;
+            deb.Click();
+        }
+
+        private void OKButton_Click(object sender, RoutedEventArgs e)
+        {
+            var errors = OnValidateData?.Invoke(this, Items);
+
+            if (errors != null && errors.Any())
+            {
+                MessageBox.Show(
+                    string.Format("The following errors have been found with your data!\nPlease correct them and try again.\n\n- {0}",
+                        string.Join("\n- ", errors)), "Validation Error");
+                return;
+            }
+            OnOK?.Invoke();
+
+            // Don't Commit the changes here, because we want to refer back to thos changes when we save the item
+            // to trigger specific processes in the database
+            //Close();
+        }
+
+        private void CancelButton_Click(object sender, RoutedEventArgs e)
+        {
+            // However, if we cancel the edits, then we can safely revert the items back to their original (loaded) state
+            foreach (var item in _items)
+                item.CancelChanges();
+
+            OnCancel?.Invoke();
+            //Close();
+        }
+
+        public void SaveItem(CancelEventArgs e)
+        {
+            OnSaveItem?.Invoke(this, e);
+        }
+
+        public bool TryFindEditor(string columnname, [NotNullWhen(true)] out IDynamicEditorControl? editor)
+        {
+            return Editor.TryFindEditor(columnname, out editor);
+        }
+        public IDynamicEditorControl FindEditor(string columnname)
+        {
+            return Editor.FindEditor(columnname);
+        }
+
+        public void EditLayout() => Editor.EditLayout();
+        public void ResetLayout() => Editor.ResetLayout();
+
+    }
+}

+ 1 - 1
InABox.DynamicGrid/FormDesigner/DynamicFormControlGrid.cs

@@ -29,7 +29,7 @@ namespace InABox.DynamicGrid
             return Items[row.Index];
         }
 
-        protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T> sort, Action<CoreTable, Exception> action)
+        protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T>? sort, Action<CoreTable?, Exception?> action)
         {
             var table = new CoreTable();
             table.LoadColumns(typeof(T));

+ 2 - 1
InABox.DynamicGrid/IDynamicGrid.cs

@@ -25,9 +25,10 @@ namespace InABox.DynamicGrid
         double FontSize { get; set; }
         void Refresh(bool columns, bool data);
 
+        void InitialiseEditorForm(IDynamicEditorForm editor, object[] items, Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false);
         bool EditItems(object[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false);
 
-        bool DirectEdit(CoreTable data);
+        //bool DirectEdit(CoreTable data);
 
         event OnFilterRecord OnFilterRecord;
         event OnCreateItem OnCreateItem;

+ 1 - 1
InABox.DynamicGrid/ScriptModel.cs

@@ -66,7 +66,7 @@ namespace InABox.DynamicGrid
 
         private static PrintOptions PrintOptions { get; } = new() { MemberDisplayFormat = MemberDisplayFormat.SeparateLines };
 
-        public event PropertyChangedEventHandler PropertyChanged;
+        public event PropertyChangedEventHandler? PropertyChanged;
 
         internal void Initialize(DocumentId id)
         {

+ 44 - 1
inabox.wpf/ImageUtils.cs

@@ -1,4 +1,5 @@
 using InABox.Core;
+using Syncfusion.Pdf.Parsing;
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 using System.Globalization;
@@ -696,7 +697,49 @@ namespace InABox.WPF
         
         public static uint ToUint(this System.Drawing.Color color) => (uint)((color.A << 24) | (color.R << 16) | (color.G << 8)  | (color.B << 0));
         public static uint ToUint(this System.Windows.Media.Color color) => (uint)((color.A << 24) | (color.R << 16) | (color.G << 8)  | (color.B << 0));
-        
+
+        public enum ImageEncoding
+        {
+            JPEG
+        }
+
+        public static ImageCodecInfo? GetEncoder(ImageFormat format)
+        {
+            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
+            foreach (ImageCodecInfo codec in codecs)
+            {
+                if (codec.FormatID == format.Guid)
+                {
+                    return codec;
+                }
+            }
+            return null;
+        }
+
+        public static List<byte[]> RenderPDFToImages(byte[] pdfData, ImageEncoding encoding = ImageEncoding.JPEG)
+        {
+            var rendered = new List<byte[]>();
+
+            PdfLoadedDocument loadeddoc = new PdfLoadedDocument(pdfData);
+            Bitmap[] images = loadeddoc.ExportAsImage(0, loadeddoc.Pages.Count - 1);
+
+            var jpgEncoder = GetEncoder(ImageFormat.Jpeg)!;
+            var quality = Encoder.Quality;
+            var encodeParams = new EncoderParameters(1);
+            encodeParams.Param[0] = new EncoderParameter(quality, 100L);
+
+            if (images != null)
+                foreach (var image in images)
+                {
+                    using (var data = new MemoryStream())
+                    {
+                        image.Save(data, jpgEncoder, encodeParams);
+                        rendered.Add(data.ToArray());
+                    }
+                }
+
+            return rendered;
+        }
     }
 
 

+ 1 - 0
inabox.wpf/InABox.Wpf.csproj

@@ -30,6 +30,7 @@
         <PackageReference Include="ColorHelper" Version="1.8.0" />
         <PackageReference Include="ControlzEx" Version="5.0.2" />
         <PackageReference Include="Extended.Wpf.Toolkit" Version="4.4.0" />
+        <PackageReference Include="Syncfusion.Pdf.Wpf" Version="20.2.0.46" />
         <PackageReference Include="Syncfusion.Tools.WPF" Version="20.2.0.46" />
         <PackageReference Include="Syncfusion.XlsIO.Wpf" Version="20.2.0.46" />
     </ItemGroup>