浏览代码

Improved property changed notifications for Shell to include cascaded changes; (otherwise binding for properties set in Entity.DoPropertyChanged weren't updating)

Kenric Nugteren 2 周之前
父节点
当前提交
679b44cc1f
共有 3 个文件被更改,包括 81 次插入28 次删除
  1. 19 12
      InABox.Avalonia/DataModels/Shell.cs
  2. 36 14
      InABox.Avalonia/DataModels/ShellColumns.cs
  3. 26 2
      InABox.Core/Objects/BaseObject.cs

+ 19 - 12
InABox.Avalonia/DataModels/Shell.cs

@@ -1,5 +1,6 @@
 using System.ComponentModel;
 using System.Runtime.CompilerServices;
+using DynamicData.Binding;
 using InABox.Clients;
 using InABox.Core;
 
@@ -55,9 +56,19 @@ namespace InABox.Avalonia
         private TEntity CheckEntity()
         {
             _entity ??= Row.ToObject<TEntity>();
+            _entity.PropertyCascaded -= _entity_PropertyChanged;
+            _entity.PropertyCascaded += _entity_PropertyChanged;
             return _entity;
         }
 
+        private void _entity_PropertyChanged(object? sender, PropertyChangedEventArgs e)
+        {
+            if(e.PropertyName is not null && Columns.FindShellProperty(e.PropertyName) is string property)
+            {
+                DoPropertyChanged(property);
+            }
+        }
+
         public TEntity Entity => CheckEntity();
         
         protected virtual void RowChanged()
@@ -157,7 +168,7 @@ namespace InABox.Avalonia
             {
                 return (T)CoreUtils.GetPropertyValue(
                     _entity,
-                    CoreUtils.GetFullPropertyName(Columns[property], ".")
+                    Columns[property]
                 );
             }
 
@@ -176,18 +187,14 @@ namespace InABox.Avalonia
         protected virtual void Set<T>(T value, bool donotify = true, [CallerMemberName] string property = null, params string[] notify)
         {
             CheckEntity();
-            CoreUtils.SetPropertyValue(
-                _entity,
-                CoreUtils.GetFullPropertyName(Columns[property], "."),
-                value
-            );
+            CoreUtils.SetPropertyValue(_entity, Columns[property], value);
             //Row.Values[Columns.IndexOf(property)] = value;
-            if (donotify)
-            {
-                DoPropertyChanged(property);
-                foreach (var _property in notify)
-                    DoPropertyChanged(_property);
-            }
+            // if (donotify)
+            // {
+            //     DoPropertyChanged(property);
+            //     foreach (var _property in notify)
+            //         DoPropertyChanged(_property);
+            // }
         }
         
 

+ 36 - 14
InABox.Avalonia/DataModels/ShellColumns.cs

@@ -6,35 +6,57 @@ using InABox.Core;
 
 namespace InABox.Avalonia
 {
+    public class ShellColumn<TEntity>
+        where TEntity : Entity
+    {
+        public int Index { get; }
+
+        public Expression<Func<TEntity, object?>> Expression { get; }
+
+        public string ColumnName { get; }
+
+        public ShellColumn(int index, Expression<Func<TEntity, object?>> expression)
+        {
+            Index = index;
+            Expression = expression;
+            ColumnName = CoreUtils.GetFullPropertyName(expression, ".");
+        }
+    }
+
     public class ShellColumns<TParent,TEntity> : IShellColumns<TEntity> where TEntity : Entity
     {
         
-        private static Dictionary<string, Tuple<int,Expression<Func<TEntity, object?>>>> _columns = new Dictionary<string, Tuple<int,Expression<Func<TEntity, object?>>>> ();
+        private static Dictionary<string, ShellColumn<TEntity>> _columns = new();
 
-        public int IndexOf(string name) => _columns[name].Item1;
+        public int IndexOf(string name) => _columns[name].Index;
         
-        public Expression<Func<TEntity, object?>> this[string name] => _columns[name].Item2;
+        public string this[string name] => _columns[name].ColumnName;
 
         public ShellColumns<TParent,TEntity> Map(string property, Expression<Func<TEntity, object?>> expression)
         {
             int iCol = _columns.Keys.Count;
-            bool bFound = _columns.TryGetValue(property, out var column);
-            try
+            if(_columns.TryGetValue(property, out var column))
             {
-                if (bFound)
-                    iCol = column.Item1;
-            }
-            catch (Exception e)
-            {
-                
+                iCol = column.Index;
             }
             
-            _columns[property] = new Tuple<int, Expression<Func<TEntity, object?>>>(iCol, expression);
-            //_columns[property] = new Tuple<int, Expression<Func<TEntity, object>>>(_columns.Keys.Count, expression);
+            _columns[property] = new(iCol, expression);
             return this;
         }
 
-        public Columns<TEntity> Columns => new Columns<TEntity>(ColumnTypeFlags.None).Add(_columns.Select(x => x.Value.Item2).ToArray());
+        public string? FindShellProperty(string entityProperty)
+        {
+            foreach(var (property, column) in _columns)
+            {
+                if(column.ColumnName == entityProperty)
+                {
+                    return property;
+                }
+            }
+            return null;
+        }
+
+        public Columns<TEntity> Columns => new Columns<TEntity>(ColumnTypeFlags.None).Add(_columns.Select(x => x.Value.ColumnName));
         
         public int Count => _columns.Count;
 

+ 26 - 2
InABox.Core/Objects/BaseObject.cs

@@ -255,7 +255,22 @@ namespace InABox.Core
         {
         }
 
+        /// <summary>
+        /// Triggered for any property change, but is not triggered if that property change triggers any other property changes.
+        /// </summary>
+        /// <remarks>
+        ///     See also <seealso cref="PropertyCascaded"/>.
+        /// </remarks>
         public event PropertyChangedEventHandler? PropertyChanged;
+
+        /// <summary>
+        /// Triggered for any property change, including property changes triggered by other properties changing.
+        /// </summary>
+        /// <remarks>
+        /// Most cases should just use <see cref="PropertyChanged"/>.
+        /// </remarks>
+        public event PropertyChangedEventHandler? PropertyCascaded;
+        
         private bool bApplyingChanges;
 
         private bool bChanged;
@@ -315,12 +330,21 @@ namespace InABox.Core
 
                     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
                 }
-                catch (Exception)
+                catch (Exception e)
                 {
-                    
+                    CoreUtils.LogException("", e);
                 }
                 bApplyingChanges = false;
             }
+
+            try
+            {
+                PropertyCascaded?.Invoke(this, new PropertyChangedEventArgs(name));
+            }
+            catch (Exception e)
+            {
+                CoreUtils.LogException("", e);
+            }
         }
 
         // This function is *only* meant to be called by EnclosedEntity and EntityLink