ソースを参照

Merge remote-tracking branch 'origin/kenric' into frank

frogsoftware 1 年間 前
コミット
9125270148

+ 106 - 24
InABox.Core/BaseObject.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -26,6 +27,83 @@ namespace InABox.Core
         public void SetObserving(bool active);
     }
 
+    public interface IOriginalValues : IEnumerable<KeyValuePair<string, object?>>
+    {
+        public object? this[string key] { get; set; }
+
+        public bool ContainsKey(string key);
+
+        public void Clear();
+
+        public bool TryGetValue(string key, out object? value);
+
+        public bool TryAdd(string key, object? value);
+
+        public void Remove(string key);
+
+        public object? GetValueOrDefault(string key)
+        {
+            if(TryGetValue(key, out object? value)) return value;
+            return null;
+        }
+    }
+    public class OriginalValues : IOriginalValues
+    {
+        private readonly ConcurrentDictionary<string, object?> Dictionary = new ConcurrentDictionary<string, object?>();
+
+        public object? this[string key] { get => Dictionary[key]; set => Dictionary[key] = value; }
+
+        public void Clear()
+        {
+            Dictionary.Clear();
+        }
+
+        public bool ContainsKey(string key) => Dictionary.ContainsKey(key);
+
+        public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
+        {
+            return Dictionary.GetEnumerator();
+        }
+
+        public bool TryGetValue(string key, out object? value)
+        {
+            return Dictionary.TryGetValue(key, out value);
+        }
+
+        public bool TryAdd(string key, object? value)
+        {
+            return Dictionary.TryAdd(key, value);
+        }
+
+        public void Remove(string key)
+        {
+            (Dictionary as IDictionary<string, object?>).Remove(key);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+    }
+
+    public interface ILoadedColumns
+    {
+        public bool Add(string key);
+
+        public bool Contains(string key);
+    }
+    public class LoadedColumns : ILoadedColumns
+    {
+        private HashSet<string> Columns = new HashSet<string>();
+
+        public bool Add(string key)
+        {
+            return Columns.Add(key);
+        }
+
+        public bool Contains(string key)
+        {
+            return Columns.Contains(key);
+        }
+    }
+
     /// <summary>
     ///     Observable object with INotifyPropertyChanged implemented
     /// </summary>
@@ -57,9 +135,7 @@ namespace InABox.Core
 
         protected virtual void Init()
         {
-            CheckOriginalValues();
-
-            LoadedColumns = new HashSet<string>();
+            LoadedColumns = CreateLoadedColumns();
 
             UserProperties = new UserProperties();
             //UserProperties.ParentType = this.GetType();
@@ -85,7 +161,7 @@ namespace InABox.Core
 
         public static bool GlobalObserving = true;
 
-        private bool _observing = true;
+        private bool _observing = false;
 
         public bool IsObserving()
         {
@@ -111,11 +187,19 @@ namespace InABox.Core
         private bool bChanged;
 
         [DoNotPersist]
-        public ConcurrentDictionary<string, object?> OriginalValues { get; set; }
+        private IOriginalValues _originalValues;
+        public IOriginalValues OriginalValues
+        {
+            get
+            {
+                _originalValues ??= CreateOriginalValues();
+                return _originalValues;
+            }
+        }
 
         [DoNotPersist]
         [DoNotSerialize]
-        public HashSet<string> LoadedColumns { get; set; }
+        public ILoadedColumns LoadedColumns { get; set; }
 
         protected virtual void SetChanged(string name, object? before, object? after)
         {
@@ -140,11 +224,15 @@ namespace InABox.Core
             }
         }
 
+        // This function is *only* meant to be called by EnclosedEntity and EntityLink
+        internal void CascadePropertyChanged(string name, object? before, object? after)
+        {
+            SetChanged(name, before, after);
+        }
 
         private bool QueryChanged()
         {
-            CheckOriginalValues();
-            if (OriginalValues.Count > 0)
+            if (OriginalValues.Any())
                 return true;
 
             foreach (var oo in DatabaseSchema.GetSubObjects(this))
@@ -173,21 +261,18 @@ namespace InABox.Core
             if (!BaseObjectExtensions.HasChanged(before, after))
                 return;
 
-            CheckOriginalValues();
             if (!OriginalValues.ContainsKey(name))
                 OriginalValues[name] = before;
             SetChanged(name, before, after);
         }
 
-        private void CheckOriginalValues()
+        protected virtual IOriginalValues CreateOriginalValues()
         {
-            if (OriginalValues == null)
-            {
-                var bObserving = _observing;
-                _observing = false;
-                OriginalValues = new ConcurrentDictionary<string, object?>();
-                _observing = bObserving;
-            }
+            return new OriginalValues();
+        }
+        protected virtual ILoadedColumns CreateLoadedColumns()
+        {
+            return new LoadedColumns();
         }
 
         public bool IsChanged()
@@ -201,13 +286,12 @@ namespace InABox.Core
             var bObs = IsObserving();
             SetObserving(false);
 
-            CheckOriginalValues();
-            foreach (var key in OriginalValues.Keys.ToArray())
+            foreach (var (key, value) in OriginalValues)
             {
                 try
                 {
                     var prop = key.Contains(".") 
-                        ? CoreUtils.GetProperty(GetType(),key) 
+                        ? CoreUtils.GetProperty(GetType(), key) 
                         : GetType().GetRuntimeProperty(key);
                     if(prop != null)
                     {
@@ -222,7 +306,7 @@ namespace InABox.Core
                     }
                     else if (UserProperties.ContainsKey(key))
                     {
-                        UserProperties[key] = OriginalValues[key];
+                        UserProperties[key] = value;
                     }
                     else
                     {
@@ -251,7 +335,6 @@ namespace InABox.Core
         {
             bApplyingChanges = true;
 
-            CheckOriginalValues();
             OriginalValues.Clear();
 
             bChanged = false;
@@ -268,8 +351,7 @@ namespace InABox.Core
             var type = GetType();
             try
             {
-                CheckOriginalValues();
-                foreach (var key in OriginalValues.Keys)
+                foreach (var (key, _) in OriginalValues)
                     try
                     {
                         if (UserProperties.ContainsKey(key))

+ 0 - 2
InABox.Core/CoreUtils.cs

@@ -875,8 +875,6 @@ namespace InABox.Core
                 //if (method == null)
                 var oldval = propertyToSet.GetValue(o);
                 propertyToSet.SetValue(o, convertedvalue, null);
-                if (o is BaseObject)
-                    ((BaseObject)o).OnPropertyChanged(propertyToSet.Name, oldval, convertedvalue);
 
                 //else
                 //    method.Invoke(o, new Object[] { convertedvalue });

+ 1 - 7
InABox.Core/Editors/Utils/DataLookupEditor.cs

@@ -48,16 +48,10 @@ namespace InABox.Core
             var sort = LookupFactory.DefineSort(Type);
             var result = client.Query(filter, columns, sort);
 
-            var generator = LookupFactory.GetLookupGenerator(parent, columnname);
             result.Columns.Add(new CoreColumn { ColumnName = "Display", DataType = typeof(string) });
             foreach (var row in result.Rows)
             {
-                var display = "";
-                if (generator != null)
-                    display = generator.FormatDisplay(row);
-                else
-                    display = LookupFactory.FormatLookup(Type, row.ToDictionary(new[] { "Display" }), Array.Empty<string>());
-                row["Display"] = display;
+                row["Display"] = LookupFactory.FormatLookup(Type, row, columnname);
             }
 
             return result;

+ 14 - 5
InABox.Core/EnclosedEntity.cs

@@ -25,18 +25,27 @@ namespace InABox.Core
             _linkedPath = path;
         }
 
+        protected override IOriginalValues CreateOriginalValues()
+        {
+            return new SubObjectOriginalValues(this);
+        }
+
+        protected override ILoadedColumns CreateLoadedColumns()
+        {
+            return new SubObjectLoadedColumns(this);
+        }
+
         public BaseObject GetLinkedParent() => _linkedParent;
 
         public string GetLinkedPath() => _linkedPath;
         
         protected override void DoPropertyChanged(string name, object? before, object? after)
         {
-            if (IsObserving())
+            LinkedProperties.GetParent(this).CascadePropertyChanged(LinkedProperties.GetPath(this) + "." + name, before, after);
+
+            if (LinkedProperties.Find(this, name, out var link, out var parent))
             {
-                if (LinkedProperties.Find(this, name, out var link, out var parent))
-                {
-                    link.Update(this, parent);
-                }
+                link.Update(this, parent);
             }
         }
     }

+ 15 - 6
InABox.Core/EntityLink.cs

@@ -46,6 +46,16 @@ namespace InABox.Core
 
         public string GetLinkedPath() => _linkedPath;
 
+        protected override IOriginalValues CreateOriginalValues()
+        {
+            return new SubObjectOriginalValues(this);
+        }
+
+        protected override ILoadedColumns CreateLoadedColumns()
+        {
+            return new SubObjectLoadedColumns(this);
+        }
+
         /*
         [Obsolete("Please supply linked Entity")]
         public EntityLink()
@@ -134,15 +144,14 @@ namespace InABox.Core
 
         protected override void DoPropertyChanged(string name, object? before, object? after)
         {
-            if (IsObserving())
+            LinkedProperties.GetParent(this).CascadePropertyChanged(LinkedProperties.GetPath(this) + "." + name, before, after);
+
+            if (LinkedProperties.Find(this, name, out var link, out var parent))
             {
-                if(LinkedProperties.Find(this, name, out var link, out var parent))
-                {
-                    link.Update(this, parent);
-                }
+                link.Update(this, parent);
             }
         }
 
-        public bool IsValid() => !ID.Equals(Guid.Empty) && Deleted.Equals(Guid.Empty);
+        public bool IsValid() => !ID.Equals(Guid.Empty);
     }
 }

+ 25 - 8
InABox.Core/ILookupDefinition.cs

@@ -83,7 +83,7 @@ namespace InABox.Core
     {
         public virtual Columns<TLookup> DefineColumns() => Columns.None<TLookup>();
 
-        public virtual string? FormatDisplay(CoreRow row) =>
+        public virtual string FormatDisplay(CoreRow row) =>
             LookupFactory.FormatLookup<TLookup>(row.ToDictionary(), new string[] { "Display" });
 
         public virtual Filter<TLookup>? DefineFilter(TEntity[] items) => null;
@@ -109,7 +109,7 @@ namespace InABox.Core
 
         IColumns ILookupDefinitionGenerator.DefineColumns() => DefineColumns();
 
-        String ILookupDefinitionGenerator.FormatDisplay(CoreRow row) => FormatDisplay(row);
+        string ILookupDefinitionGenerator.FormatDisplay(CoreRow row) => FormatDisplay(row);
 
         IFilter? ILookupDefinitionGenerator.DefineFilter(BaseObject[] items) => DefineFilter(items as TEntity[]);
 
@@ -319,19 +319,15 @@ namespace InABox.Core
         {
             var columns = Columns.None(TLookup);
             
-            
             var property = GetLinkProperty(TEntity, column);
             if(property != null)
             {
-                
                 var prefix = property.Name + ".";
-                
-                columns.Add(column.Substring(prefix.Length));
 
                 // Add all required columns of the entity link.
-                foreach(var prop in DatabaseSchema.Properties(property.PropertyType).Where(x => x.Required))
+                foreach(var prop in DatabaseSchema.Properties(TEntity).Where(x => x.Required && x.Name.StartsWith(prefix)))
                 {
-                    columns.Add(prop.Name);
+                    columns.Add(prop.Name[prefix.Length..]);
                 }
 
                 // Add all columns which are associated with linked properties.
@@ -364,6 +360,10 @@ namespace InABox.Core
             {
                 columns = Columns.Create(TLookup, ColumnTypeFlags.IncludeID | ColumnTypeFlags.IncludeVisible);
             }
+            else
+            {
+                columns.Add(nameof(Entity.ID));
+            }
 
             return columns;
         }
@@ -438,6 +438,19 @@ namespace InABox.Core
             return DefaultFormatLookup(values, exclude);
         }
 
+        public static string FormatLookup(Type TEntity, CoreRow row, string column)
+        {
+            var generator = GetLookupGenerator(TEntity, column);
+            if(generator != null)
+            {
+                return generator.FormatDisplay(row);
+            }
+            else
+            {
+                return FormatLookup(TEntity, row.ToDictionary(new[] { "Display" }), Array.Empty<string>());
+            }
+        }
+
         public static ISortOrder? DefineSort(Type T)
         {
             return GetDefinition(T)?.DefineSortOrder();
@@ -489,6 +502,10 @@ namespace InABox.Core
 
         public static string FormatLookup<T>(Dictionary<string, object?> values, IEnumerable<string> exclude) => FormatLookup(typeof(T), values, exclude);
 
+        public static string FormatLookup<TEntity, TLookup, TLookupLink>(CoreRow row, Expression<Func<TEntity, TLookupLink>> column)
+            where TLookupLink : IEntityLink<TLookup>
+            => FormatLookup(typeof(TEntity), row, CoreUtils.GetFullPropertyName(column, "."));
+
         public static void OnCreateItem<TEntity, TLookup, TLookupLink>(Expression<Func<TEntity, TLookupLink>> column, TEntity[] items, TLookup item)
             where TLookupLink : IEntityLink<TLookup>
             where TEntity : BaseObject

+ 130 - 28
InABox.Core/LinkedProperties.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
@@ -17,30 +18,129 @@ namespace InABox.Core
         void SetLinkedPath(string path);
     }
 
-    // public interface IResult
-    // {
-    //     object? Value { get; set; }
-    //     bool Cancel { get; set; }
-    // }
-    //
-    // public class Result<TType> : IResult
-    // {
-    //     public TType Value { get; set; }
-    //
-    //     object? IResult.Value {
-    //         get => this.Value;
-    //         set => this.Value = (value is TType _type ? _type : default)!;
-    //     }
-    //     
-    //     public bool Cancel { get; set; }
-    //
-    //     public Result(TType value, bool cancel = false)
-    //     {
-    //         Value = value;
-    //         Cancel = cancel;
-    //     }
-    // }
+    public class SubObjectOriginalValues : IOriginalValues
+    {
+        private ISubObject Object { get; set; }
+
+        private IOriginalValues? _rootDictionary;
+        private IOriginalValues RootDictionary
+        {
+            get
+            {
+                _rootDictionary ??= LinkedProperties.GetParent(Object).OriginalValues;
+                return _rootDictionary;
+            }
+        }
+
+        private string? _rootPath;
+        /// <summary>
+        /// Path of this sub object according to the root, suffixed with a "."
+        /// </summary>
+        private string RootPath
+        {
+            get
+            {
+                _rootPath ??= $"{LinkedProperties.GetPath(Object)}.";
+                return _rootPath;
+            }
+        }
+
+        public SubObjectOriginalValues(ISubObject obj)
+        {
+            Object = obj;
+        }
+
+        private string ChangeKey(string key) => $"{RootPath}{key}";
+
+        public bool ContainsKey(string key)
+        {
+            return RootDictionary.ContainsKey(ChangeKey(key));
+        }
+
+        public void Clear()
+        {
+            foreach(var (k, v) in RootDictionary)
+            {
+                if (k.StartsWith(RootPath))
+                {
+                    RootDictionary.Remove(k);
+                }
+            }
+        }
+
+        public void Remove(string key)
+        {
+            RootDictionary.Remove(ChangeKey(key));
+        }
+
+        public bool TryAdd(string key, object? value)
+        {
+            return RootDictionary.TryAdd(ChangeKey(key), value);
+        }
+
+        public bool TryGetValue(string key, out object? value)
+        {
+            return RootDictionary.TryGetValue(ChangeKey(key), out value);
+        }
+
+        public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
+        {
+            return RootDictionary.Where(x => x.Key.StartsWith(RootPath)).GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+        public object? this[string key]
+        {
+            get => RootDictionary[ChangeKey(key)];
+            set => RootDictionary[ChangeKey(key)] = value;
+        }
+    }
+
+    public class SubObjectLoadedColumns : ILoadedColumns
+    {
+        private ISubObject Object { get; set; }
+
+        private ILoadedColumns? _rootSet;
+        private ILoadedColumns RootSet
+        {
+            get
+            {
+                _rootSet ??= LinkedProperties.GetParent(Object).LoadedColumns;
+                return _rootSet;
+            }
+        }
+
+        private string? _rootPath;
+        /// <summary>
+        /// Path of this sub object according to the root, suffixed with a "."
+        /// </summary>
+        private string RootPath
+        {
+            get
+            {
+                _rootPath ??= $"{LinkedProperties.GetPath(Object)}.";
+                return _rootPath;
+            }
+        }
+
+        public SubObjectLoadedColumns(ISubObject obj)
+        {
+            Object = obj;
+        }
+
+        private string ChangeKey(string key) => $"{RootPath}{key}";
+
+        public bool Add(string key)
+        {
+            return RootSet.Add(ChangeKey(key));
+        }
 
+        public bool Contains(string key)
+        {
+            return RootSet.Contains(ChangeKey(key));
+        }
+    }
 
     public static class LinkedProperties
     {
@@ -71,12 +171,12 @@ namespace InABox.Core
             return filtered;
         }*/
 
-        private static BaseObject GetParent(ISubObject link)
+        public static BaseObject GetParent(ISubObject link)
         {
             while (true)
             {
                 var parent = link.GetLinkedParent();
-                if(parent is IEntityLink parentLink)
+                if(parent is ISubObject parentLink)
                 {
                     link = parentLink;
                 }
@@ -87,13 +187,13 @@ namespace InABox.Core
             }
         }
 
-        private static string GetPath(ISubObject link)
+        public static string GetPath(ISubObject link)
         {
             var path = link.GetLinkedPath();
             while (true)
             {
                 var parent = link.GetLinkedParent();
-                if (parent is IEntityLink parentLink)
+                if (parent is ISubObject parentLink)
                 {
                     link = parentLink;
                     path = $"{link.GetLinkedPath()}.{path}";
@@ -135,7 +235,9 @@ namespace InABox.Core
             if(!_LinkedProperties.TryGetValue(parent.GetType(), out var props))
                 return false;
             var path = GetPath(subObject);
-            property = props.FirstOrDefault(x => String.Equals($"{x.Path}{(String.IsNullOrWhiteSpace(x.Path) ? "" : ".")}{x.Source}", $"{path}{(String.IsNullOrWhiteSpace(path) ? "" : ".")}{name}"));
+            property = props.FirstOrDefault(x => string.Equals(
+                $"{x.Path}{(x.Path.IsNullOrWhiteSpace() ? "" : ".")}{x.Source}",
+                $"{path}{(path.IsNullOrWhiteSpace() ? "" : ".")}{name}"));
             return property != null;
         }
         

+ 124 - 113
inabox.wpf/DynamicGrid/DynamicEntityFormGrid.cs

@@ -9,152 +9,163 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Windows.Media.Imaging;
 
-namespace InABox.DynamicGrid
+namespace InABox.DynamicGrid;
+
+public class DynamicEntityFormGrid<TForm, TEntity, TEntityLink> : DynamicDataGrid<TForm>
+    where TForm : EntityForm<TEntity, TEntityLink, TForm>, new()
+    where TEntity : Entity
+    where TEntityLink : IEntityLink<TEntity>, new()
 {
-    public class DynamicEntityFormGrid<TForm, TEntity, TEntityLink> : DynamicDataGrid<TForm>
-        where TForm : EntityForm<TEntity, TEntityLink, TForm>, new()
-        where TEntity : Entity
-        where TEntityLink : IEntityLink<TEntity>, new()
-    {
-        protected virtual TEntity Entity { get; set; }
+    protected virtual TEntity Entity { get; set; }
 
-        public DynamicEntityFormGrid()
-        {
-            OnBeforeSave += BeforeSave;
-            OnCustomiseEditor += DynamicEntityFormGrid_OnCustomiseEditor;
-
-            ActionColumns.Add(new DynamicImageColumn(EditImage, EditClick));
-            if (DynamicGridUtils.PreviewReport != null)
-                ActionColumns.Add(new DynamicImageColumn(PrintImage, PrintClick));
-            HiddenColumns.Add(x => x.FormCompleted);
-            HiddenColumns.Add(x => x.Form.ID);
-        }
+    public bool EditOnAdd { get; set; }
 
-        public DynamicEntityFormGrid(TEntity entity) : this()
-        {
-            Entity = entity;
-        }
+    public DynamicEntityFormGrid()
+    {
+        OnBeforeSave += BeforeSave;
+        OnCustomiseEditor += DynamicEntityFormGrid_OnCustomiseEditor;
+
+        ActionColumns.Add(new DynamicImageColumn(EditImage, EditClick));
+        if (DynamicGridUtils.PreviewReport != null)
+            ActionColumns.Add(new DynamicImageColumn(PrintImage, PrintClick));
+        HiddenColumns.Add(x => x.FormCompleted);
+        HiddenColumns.Add(x => x.Form.ID);
+    }
 
-        private void DynamicEntityFormGrid_OnCustomiseEditor(IDynamicEditorForm sender, TForm[]? items, DynamicGridColumn column, BaseEditor editor)
-        {
-            if(column.ColumnName == "Form.ID")
-            {
-                editor.Editable = Editable.Disabled;
-            }
-        }
+    public DynamicEntityFormGrid(TEntity entity) : this()
+    {
+        Entity = entity;
+    }
 
-        protected virtual void OnFormCreated(TForm form)
+    private void DynamicEntityFormGrid_OnCustomiseEditor(IDynamicEditorForm sender, TForm[]? items, DynamicGridColumn column, BaseEditor editor)
+    {
+        if(column.ColumnName == "Form.ID")
         {
+            editor.Editable = Editable.Disabled;
         }
+    }
 
-        protected override void DoAdd(bool OpenEditorOnDirectEdit = false)
-        {
-            var filter = LookupFactory.DefineChildFilter<TEntity, DigitalForm>(new TEntity[] { Entity })
-                ?? LookupFactory.DefineLookupFilter<TForm, DigitalForm, DigitalFormLink>(x => x.Form, Array.Empty<TForm>());
-            var columns = LookupFactory.DefineLookupColumns<TForm, DigitalForm, DigitalFormLink>(x => x.Form);
+    protected override void DoAdd(bool OpenEditorOnDirectEdit = false)
+    {
+        var filter = LookupFactory.DefineChildFilter<TEntity, DigitalForm>(new TEntity[] { Entity })
+            ?? LookupFactory.DefineLookupFilter<TForm, DigitalForm, DigitalFormLink>(x => x.Form, Array.Empty<TForm>());
+        var columns = LookupFactory.DefineLookupColumns<TForm, DigitalForm, DigitalFormLink>(x => x.Form);
 
-            var select = new MultiSelectDialog<DigitalForm>(
-                filter,
-                columns.Add(x => x.Description),
-                false);
+        var select = new MultiSelectDialog<DigitalForm>(
+            filter,
+            columns.Add(x => x.Description),
+            false);
 
-            if (select.ShowDialog() == true)
+        if (select.ShowDialog() == true)
+        {
+            var digitalForm = select.Items().FirstOrDefault();
+            if(digitalForm is not null)
             {
-                var digitalForm = select.Items().FirstOrDefault();
-                if(digitalForm is not null)
+                var form = new TForm
                 {
-                    var form = new TForm
-                    {
-                        Description = digitalForm.Description
-                    };
-                    form.Form.ID = digitalForm.ID;
-                    form.Parent.ID = Entity.ID;
+                    Description = digitalForm.Description
+                };
+                form.Form.ID = digitalForm.ID;
+                form.Parent.ID = Entity.ID;
 
+                var refresh = false;
+                if (EditOnAdd)
+                {
+                    if (DynamicFormEditWindow.EditDigitalForm(form, out var dataModel))
+                    {
+                        dataModel.Update(null);
+                        refresh = true;
+                    }
+                }
+                else
+                {
                     SaveItem(form);
+                    refresh = true;
+                }
 
-                    OnFormCreated(form);
-
+                if (refresh)
+                {
                     Refresh(false, true);
                     DoChanged();
                 }
             }
         }
+    }
 
-        public override TForm CreateItem()
-        {
-            var result = base.CreateItem();
-            result.Parent.ID = Entity.ID;
-            return result;
-        }
+    public override TForm CreateItem()
+    {
+        var result = base.CreateItem();
+        result.Parent.ID = Entity.ID;
+        return result;
+    }
 
-        protected override void Reload(
-            Filters<TForm> criteria, Columns<TForm> columns, ref SortOrder<TForm>? sort, 
-            CancellationToken token, Action<CoreTable?, Exception?> action)
-        {
-            criteria.Add(new Filter<TForm>(x => x.Parent.ID).IsEqualTo(Entity.ID));
-            base.Reload(criteria, columns, ref sort, token, action);
-        }
+    protected override void Reload(
+        Filters<TForm> criteria, Columns<TForm> columns, ref SortOrder<TForm>? sort, 
+        CancellationToken token, Action<CoreTable?, Exception?> action)
+    {
+        criteria.Add(new Filter<TForm>(x => x.Parent.ID).IsEqualTo(Entity.ID));
+        base.Reload(criteria, columns, ref sort, token, action);
+    }
 
-        private void BeforeSave(IDynamicEditorForm editor, BaseObject[] items)
+    private void BeforeSave(IDynamicEditorForm editor, BaseObject[] items)
+    {
+        foreach(var item in items.Cast<TForm>())
         {
-            foreach(var item in items.Cast<TForm>())
-            {
-                item.Parent.ID = Entity.ID;
-            }
+            item.Parent.ID = Entity.ID;
         }
+    }
 
-        private BitmapImage PrintImage(CoreRow? arg)
-        {
-            return Wpf.Resources.print.AsBitmapImage();
-        }
+    private BitmapImage PrintImage(CoreRow? arg)
+    {
+        return Wpf.Resources.print.AsBitmapImage();
+    }
 
-        private bool PrintClick(CoreRow? arg)
-        {
-            if (arg is null) return false;
+    private bool PrintClick(CoreRow? arg)
+    {
+        if (arg is null) return false;
 
-            var formid = arg.Get<TForm, Guid>(x => x.Form.ID);
-            var model = new DigitalFormReportDataModel<TForm>(new Filter<TForm>("Parent.ID").IsEqualTo(Entity.ID), formid);
-            var section = formid.ToString();
+        var formid = arg.Get<TForm, Guid>(x => x.Form.ID);
+        var model = new DigitalFormReportDataModel<TForm>(new Filter<TForm>("Parent.ID").IsEqualTo(Entity.ID), formid);
+        var section = formid.ToString();
 
-            // TODO: This is a hack
-            DynamicGridUtils.PrintMenu?.Invoke(null, section, model, true);
+        // TODO: This is a hack
+        DynamicGridUtils.PrintMenu?.Invoke(null, section, model, true);
 
-            return false;
-        }
+        return false;
+    }
 
-        private BitmapImage EditImage(CoreRow? arg)
-        {
-            if (arg == null)
-                return Wpf.Resources.pencil.AsBitmapImage();
-            var completed = arg.Get<TForm, DateTime>(x => x.FormCompleted);
-            return completed.IsEmpty() ? Wpf.Resources.pencil.AsBitmapImage() : Wpf.Resources.view.AsBitmapImage();
-        }
+    private BitmapImage EditImage(CoreRow? arg)
+    {
+        if (arg == null)
+            return Wpf.Resources.pencil.AsBitmapImage();
+        var completed = arg.Get<TForm, DateTime>(x => x.FormCompleted);
+        return completed.IsEmpty() ? Wpf.Resources.pencil.AsBitmapImage() : Wpf.Resources.view.AsBitmapImage();
+    }
 
-        private bool EditClick(CoreRow? arg)
+    private bool EditClick(CoreRow? arg)
+    {
+        if (arg is null) return false;
+
+        var item = LoadItem(arg);
+
+        var form = new Client<TForm>()
+            .Query(
+                new Filter<TForm>(x => x.ID).IsEqualTo(item.ID),
+                Columns.None<TForm>().Add(x => x.ID)
+                    .Add(x => x.FormData)
+                    .Add(x => x.BlobData)
+                    .Add(x => x.FormCompleted)
+                    .Add(x => x.FormCompletedBy.ID)
+                    .Add(x => x.Form.ID)
+                    .Add(x => x.Parent.ID))
+            .Rows.FirstOrDefault()?.ToObject<TForm>();
+        if (form is null) return false;
+
+        if (DynamicFormEditWindow.EditDigitalForm(form, out var dataModel))
         {
-            if (arg is null) return false;
-
-            var item = LoadItem(arg);
-
-            var form = new Client<TForm>()
-                .Query(
-                    new Filter<TForm>(x => x.ID).IsEqualTo(item.ID),
-                    Columns.None<TForm>().Add(x => x.ID)
-                        .Add(x => x.FormData)
-                        .Add(x => x.BlobData)
-                        .Add(x => x.FormCompleted)
-                        .Add(x => x.FormCompletedBy.ID)
-                        .Add(x => x.Form.ID)
-                        .Add(x => x.Parent.ID))
-                .Rows.FirstOrDefault()?.ToObject<TForm>();
-            if (form is null) return false;
-
-            if (DynamicFormEditWindow.EditDigitalForm(form, out var dataModel))
-            {
-                dataModel.Update(null);
-                return true;
-            }
-            return false;
+            dataModel.Update(null);
+            return true;
         }
+        return false;
     }
 }

+ 0 - 2
inabox.wpf/DynamicGrid/DynamicGridUtils.cs

@@ -372,8 +372,6 @@ public static class DynamicGridUtils
                         var getter = prop.Getter();
                         var oldvalue = getter != null ? getter.Invoke(item) : CoreUtils.GetPropertyValue(item, name);
 
-                        item.OnPropertyChanged(name, oldvalue, value);
-
                         var setter = prop.Setter();
                         if (setter != null && value != null)
                             setter.Invoke(item, value);