Przeglądaj źródła

Allowing for tree grid to have a key type that is not a Guid.

Kenric Nugteren 7 miesięcy temu
rodzic
commit
7aee9c02c0

+ 31 - 29
InABox.Core/CoreTreeNodes.cs

@@ -7,15 +7,15 @@ using System.Linq.Expressions;
 
 namespace InABox.Core
 {
-    public class CoreTreeNode : INotifyPropertyChanged
+    public class CoreTreeNode<TKey> : INotifyPropertyChanged
     {
 
-        private CoreTreeNodes _owner;
+        private CoreTreeNodes<TKey> _owner;
         
-        public ObservableCollection<CoreTreeNode> Children => new ObservableCollection<CoreTreeNode>(_owner.GetChildrenOfParent(_id));
+        public ObservableCollection<CoreTreeNode<TKey>> Children => new ObservableCollection<CoreTreeNode<TKey>>(_owner.GetChildrenOfParent(_id));
         
-        private Guid _id;
-        public Guid ID 
+        private TKey _id;
+        public TKey ID 
         {
             get { return _id; }
             set
@@ -25,8 +25,8 @@ namespace InABox.Core
             }
         }
         
-        private Guid _parent;
-        public Guid Parent 
+        private TKey _parent;
+        public TKey Parent 
         {
             get { return _parent; }
             set
@@ -76,19 +76,19 @@ namespace InABox.Core
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
         }
 
-        public CoreTreeNode(CoreTreeNodes owner, CoreRow row)
+        public CoreTreeNode(CoreTreeNodes<TKey> owner, CoreRow row)
         {
             _owner = owner;
             _row = row;
         }
 
-        public CoreTreeNode(CoreTreeNodes owner, Guid id, Guid parent, CoreRow row) : this(owner, row)
+        public CoreTreeNode(CoreTreeNodes<TKey> owner, TKey id, TKey parent, CoreRow row) : this(owner, row)
         {
             _id = id;
             _parent = parent;
         }
 
-        public CoreTreeNode? GetParent() => _owner[_parent];
+        public CoreTreeNode<TKey>? GetParent() => _owner[_parent];
 
         public int Index()
         {
@@ -122,40 +122,42 @@ namespace InABox.Core
         
     }
 
-    public class CoreTreeNodes 
+    public class CoreTreeNodes<TKey>
     {
+        private List<CoreTreeNode<TKey>> _nodes;
+        private Dictionary<TKey, CoreTreeNode<TKey>> _nodeMap;
+        
+        public TKey DefaultKey { get; set; }
 
-        private List<CoreTreeNode> _nodes;
-        private Dictionary<Guid, CoreTreeNode> _nodeMap;
-
-        public ObservableCollection<CoreTreeNode> Nodes => new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
+        public ObservableCollection<CoreTreeNode<TKey>> Nodes => new ObservableCollection<CoreTreeNode<TKey>>(_nodes.Where(x => Equals(x.Parent, Guid.Empty)));
         
-        public CoreTreeNode? this[Guid id] => _nodes.FirstOrDefault(x => x.ID == id);
+        public CoreTreeNode<TKey>? this[TKey id] => _nodeMap.GetValueOrDefault(id);
         
-        public CoreTreeNodes()
+        public CoreTreeNodes(TKey defaultKey)
         {
-            _nodes = new List<CoreTreeNode>();
-            _nodeMap = new Dictionary<Guid, CoreTreeNode>();
+            _nodes = new List<CoreTreeNode<TKey>>();
+            _nodeMap = new Dictionary<TKey, CoreTreeNode<TKey>>();
+            DefaultKey = defaultKey;
         }
 
-        public CoreTreeNode Add(Guid id, Guid parent, CoreRow row)
+        public CoreTreeNode<TKey> Add(TKey id, TKey parent, CoreRow row)
         {
-            var node = new CoreTreeNode(this, id, parent, row);
+            var node = new CoreTreeNode<TKey>(this, id, parent, row);
             _nodes.Add(node);
             _nodeMap[id] = node;
             return node;
         }
 
-        public CoreTreeNode Find(Guid id) => _nodeMap.GetValueOrDefault(id);
+        public CoreTreeNode<TKey> Find(TKey id) => _nodeMap.GetValueOrDefault(id);
 
-        public CoreTreeNode? Find(CoreRow row) => _nodes.FirstOrDefault(x => x.Row == row);
+        public CoreTreeNode<TKey>? Find(CoreRow row) => _nodes.FirstOrDefault(x => x.Row == row);
 
         /// <summary>
         /// Gets a given node and recursively all its children given by <paramref name="id"/>.
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
-        public IEnumerable<CoreTreeNode> GetChildren(Guid id)
+        public IEnumerable<CoreTreeNode<TKey>> GetChildren(TKey id)
         {
             if(!_nodeMap.TryGetValue(id, out var node))
             {
@@ -173,12 +175,12 @@ namespace InABox.Core
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
-        public IEnumerable<CoreTreeNode> GetChildrenOfParent(Guid id)
+        public IEnumerable<CoreTreeNode<TKey>> GetChildrenOfParent(TKey id)
         {
-            return _nodes.Where(x => x.Parent.Equals(id) && (x.ID != id));
+            return _nodes.Where(x => object.Equals(x.Parent, id) && !object.Equals(x.ID, id));
         }
 
-        public void Load<T>(CoreTable table, Expression<Func<T, Guid>> id, Expression<Func<T, Guid>> parentid)
+        public void Load<T>(CoreTable table, Expression<Func<T, TKey>> id, Expression<Func<T, TKey>> parentid)
         {
             _nodes.Clear();
             foreach (var row in table.Rows)
@@ -189,10 +191,10 @@ namespace InABox.Core
             }
         }
         
-        public delegate void ColumnChangedEventHandler(CoreTreeNode node, string column);
+        public delegate void ColumnChangedEventHandler(CoreTreeNode<TKey> node, string column);
         public event ColumnChangedEventHandler? ColumnChanged;
 
-        internal void DoColumnChanged(CoreTreeNode node, string column)
+        internal void DoColumnChanged(CoreTreeNode<TKey> node, string column)
         {
             ColumnChanged?.Invoke(node, column);
         }

+ 20 - 2
InABox.Core/DatabaseSchema/DatabaseSchema.cs

@@ -2,6 +2,7 @@
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Reflection;
@@ -354,17 +355,34 @@ namespace InABox.Core
         /// <param name="type"></param>
         /// <returns></returns>
         public static IEnumerable<IProperty> Properties<T>() => Properties(typeof(T));
-        
+
         public static IProperty? Property(Type type, string name)
         {
             var prop = CheckPropertiesInternal(type)?.GetValueOrDefault(name);
 
-            // Walk up the inheritance tree, see if an ancestor has this property
+            // Walk up the inheritance tree, see if an ancestor has this property.
+            // KENRIC: not sure if this is necessary.
             if (prop == null && type.BaseType != null)
                 prop = Property(type.BaseType, name);
+
             return prop;
         }
         public static IProperty? Property<T>(Expression<Func<T, object?>> expression) => Property(typeof(T), CoreUtils.GetFullPropertyName(expression, "."));
         public static IProperty? Property<T, TType>(Expression<Func<T, TType>> expression) => Property(typeof(T), CoreUtils.GetFullPropertyName(expression, "."));
+
+        public static IProperty PropertyStrict(Type type, string name) => Property(type, name) ?? throw new PropertyNotFoundException(type, name);
+
+        public class PropertyNotFoundException : Exception
+        {
+            public Type Type { get; set; }
+
+            public string Property { get; set; }
+
+            public PropertyNotFoundException(Type T, string property) : base($"Property '{property}' not found on type {T.FullName}")
+            {
+                Type = T;
+                Property = property;
+            }
+        } 
     }
 }

+ 1 - 1
InABox.Core/Objects/ILookupDefinition.cs

@@ -587,7 +587,7 @@ namespace InABox.Core
 
         public abstract Filter<TLookup>? DefineFilter();
 
-        public abstract SortOrder<TLookup> DefineSortOrder();
+        public abstract SortOrder<TLookup>? DefineSortOrder();
     }
 
     public abstract class EntityLookup<TLookup> : BaseObjectLookup<TLookup> where TLookup : Entity, new()

+ 19 - 1
InABox.Core/Query/Column.cs

@@ -83,11 +83,25 @@ namespace InABox.Core
                 if (Expression is null)
                     throw new Exception($"Expression [{Property}] may not be null");
                 if (Expression is IndexExpression)
-                    return DatabaseSchema.Property(typeof(T), Property).PropertyType;
+                    return PropertyDefinition.PropertyType;
                 return Expression.Type;
             }
         }
 
+        private IProperty? _propertyDefinition;
+        public IProperty PropertyDefinition
+        {
+            get
+            {
+                _propertyDefinition ??= DatabaseSchema.PropertyStrict(typeof(T), Property);
+                return _propertyDefinition;
+            }
+            set
+            {
+                _propertyDefinition = value;
+            }
+        }
+
         public string Property { get; private set; }
 
         public Expression Expression { get; private set; }
@@ -104,6 +118,7 @@ namespace InABox.Core
         {
             Property = property.Name;
             Expression = property.Expression();
+            PropertyDefinition = property;
         }
 
         public Column(Expression<Func<T, object?>> expression)
@@ -118,7 +133,10 @@ namespace InABox.Core
 
             var iprop = DatabaseSchema.Property(typeof(T), property);
             if (iprop != null)
+            {
+                PropertyDefinition = iprop;
                 Expression = iprop.Expression();
+            }
             else
                 Expression = CoreUtils.CreateMemberExpression(typeof(T), property);
         }

+ 1 - 1
inabox.wpf/DynamicGrid/DynamicGridCommon.cs

@@ -449,7 +449,7 @@ public delegate void EntitySaveEvent(IDynamicEditorForm editor, BaseObject[] ite
 
 public delegate bool DynamicGridButtonClickEvent(Button button, CoreRow[] rows);
 
-public delegate void OnContextMenuOpening(CoreTreeNode? node, ContextMenu menu);
+public delegate void OnContextMenuOpening<TKey>(CoreTreeNode<TKey>? node, ContextMenu menu);
 
 
 public class DynamicGridSelectionEventArgs : CancelEventArgs

+ 106 - 41
inabox.wpf/DynamicGrid/UIComponent/DynamicGridTreeUIComponent.cs

@@ -25,6 +25,8 @@ using Syncfusion.UI.Xaml.TreeGrid.Filtering;
 using Syncfusion.UI.Xaml.TreeGrid.Cells;
 using System.Windows.Controls.Primitives;
 using NPOI.OpenXmlFormats.Dml;
+using System.Windows.Navigation;
+using NavigationMode = Syncfusion.UI.Xaml.Grid.NavigationMode;
 
 namespace InABox.DynamicGrid;
 
@@ -43,7 +45,7 @@ public enum DynamicTreeGridExpandMode
     None
 }
 
-public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynamicGridGridUIComponent<T>
+public class DynamicGridTreeUIComponent<T, TKey> : IDynamicGridUIComponent<T>, IDynamicGridGridUIComponent<T>
     where T : BaseObject, new()
 {
     private IDynamicGridUIComponentParent<T> _parent;
@@ -60,20 +62,32 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
             CellFontStyleConverter = new DynamicGridCellStyleConverter<System.Windows.FontStyle?>(Parent, GetCellFontStyle);
             CellFontWeightConverter = new DynamicGridCellStyleConverter<System.Windows.FontWeight?>(Parent, GetCellFontWeight);
 
-            Parent.AddHiddenColumn(IDColumn.Property);
-            Parent.AddHiddenColumn(ParentColumn.Property);
+            if(IDColumn is not null)
+            {
+                Parent.AddHiddenColumn(IDColumn.Property);
+            }
+            if(ParentColumn is not null)
+            {
+                Parent.AddHiddenColumn(ParentColumn.Property);
+            }
         }
     }
 
-    private Column<T> IDColumn;
-    private Column<T> ParentColumn;
+    private readonly TKey NullKey;
+
+    private readonly Func<CoreRow, TKey>? IDKey;
+    private readonly Func<CoreRow, TKey>? ParentKey;
+    private readonly Action<T, TKey>? SetParentKey;
+
+    private readonly Column<T>? IDColumn;
+    private readonly Column<T>? ParentColumn;
 
     private ContextMenu _menu;
     private SfTreeGrid _tree;
     private Grid _summaryRow;
     private readonly ContextMenu ColumnsMenu;
 
-    public event OnContextMenuOpening OnContextMenuOpening;
+    public event OnContextMenuOpening<TKey> OnContextMenuOpening;
 
     FrameworkElement IDynamicGridUIComponent<T>.Control => _tree;
 
@@ -185,11 +199,23 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
     private DynamicGridCellStyleConverter<System.Windows.FontStyle?> CellFontStyleConverter;
     private DynamicGridCellStyleConverter<System.Windows.FontWeight?> CellFontWeightConverter;
 
-    public DynamicGridTreeUIComponent(Expression<Func<T, Guid>> idColumn, Expression<Func<T, Guid>> parentIDColumn)
+    public DynamicGridTreeUIComponent(Func<CoreRow, TKey> idKey, Func<CoreRow, TKey> parentKey, TKey nullKey, Action<T, TKey> setParentKey): this()
+    {
+        IDKey = idKey;
+        ParentKey = parentKey;
+        NullKey = nullKey;
+        SetParentKey = setParentKey;
+    }
+
+    public DynamicGridTreeUIComponent(Expression<Func<T, TKey>> idColumn, Expression<Func<T, TKey>> parentIDColumn, TKey nullKey): this()
     {
         IDColumn = new Column<T>(CoreUtils.GetFullPropertyName(idColumn, "."));
         ParentColumn = new Column<T>(CoreUtils.GetFullPropertyName(parentIDColumn, "."));
+        NullKey = nullKey;
+    }
 
+    private DynamicGridTreeUIComponent()
+    {
         ColumnsMenu = new ContextMenu();
         ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening;
 
@@ -219,7 +245,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
         _menu = new ContextMenu();
         var additem = new MenuItem() { Header = "Add Child Folder" };
-        additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as CoreTreeNode)!.ID, true); };
+        additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as CoreTreeNode<TKey>)!.ID, true); };
         _menu.Items.Add(additem);
 
         _tree.ContextMenuOpening += _tree_ContextMenuOpening;
@@ -378,9 +404,9 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         });
     }
 
-    private class TreeGridSelectionControllerExt(SfTreeGrid treeGrid, DynamicGridTreeUIComponent<T> grid) : TreeGridRowSelectionController(treeGrid)
+    private class TreeGridSelectionControllerExt(SfTreeGrid treeGrid, DynamicGridTreeUIComponent<T, TKey> grid) : TreeGridRowSelectionController(treeGrid)
     {
-        private DynamicGridTreeUIComponent<T> Grid = grid;
+        private DynamicGridTreeUIComponent<T, TKey> Grid = grid;
 
         public override bool HandleKeyDown(KeyEventArgs args)
         {
@@ -398,7 +424,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     #region Public Interface
 
-    public IEnumerable<CoreRow> GetChildren(Guid id)
+    public IEnumerable<CoreRow> GetChildren(TKey id)
     {
         return Nodes.GetChildren(id).Select(x => MapRow(x.Row)).NotNull();
     }
@@ -407,12 +433,12 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     #region Input
 
-    private CoreTreeNode? GetNodeFromIndex(int rowIndex)
+    private CoreTreeNode<TKey>? GetNodeFromIndex(int rowIndex)
     {
         // Syncfusion has given us the row index, so it also will give us the correct row, after sorting.
         // Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView.
         var row = _tree.GetNodeAtRowIndex(rowIndex);
-        return row.Item as CoreTreeNode;
+        return row.Item as CoreTreeNode<TKey>;
     }
 
     private CoreRow? GetRowFromIndex(int rowIndex)
@@ -486,7 +512,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
     {
         get
         {
-            return _tree.SelectedItems.OfType<CoreTreeNode>()
+            return _tree.SelectedItems.OfType<CoreTreeNode<TKey>>()
                 .Select(x => GetRow(x)).NotNull().ToArray();
         }
         set
@@ -494,7 +520,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
             _tree.SelectedItems.Clear();
             foreach (var row in value)
             {
-                _tree.SelectedItems.Add(Nodes.Find(row.Get<Guid>(IDColumn.Property)));
+                _tree.SelectedItems.Add(Nodes.Find(GetIDKey(row)));
             }
         }
     }
@@ -821,7 +847,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         _menu.Items.Clear();
         if (OnContextMenuOpening is not null)
         {
-            OnContextMenuOpening.Invoke((_tree.SelectedItem as CoreTreeNode)!, _menu);
+            OnContextMenuOpening.Invoke((_tree.SelectedItem as CoreTreeNode<TKey>)!, _menu);
             if(_menu.Items.Count == 0)
             {
                 e.Handled = true;
@@ -831,7 +857,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         {
             if (Parent.Options.AddRows)
             {
-                _menu.AddItem("Add Item", null, (_tree.SelectedItem as CoreTreeNode)!.ID, (id) => DoAddItem(id, true));
+                _menu.AddItem("Add Item", null, (_tree.SelectedItem as CoreTreeNode<TKey>)!.ID, (id) => DoAddItem(id, true));
             }
         }
     }
@@ -840,14 +866,14 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     #region CRUD
 
-    protected T DoCreateItem(Guid parent)
+    protected T DoCreateItem(TKey parent)
     {
         var result = Parent.CreateItem();
-        CoreUtils.SetPropertyValue(result, ParentColumn.Property, parent);
+        SetParent(result, parent);
         return result;
     }
     
-    protected void DoAddItem(Guid id, bool edit)
+    protected void DoAddItem(TKey id, bool edit)
     {
         try
         {
@@ -877,7 +903,50 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     #region Rows
 
-    private CoreRow? GetRow(CoreTreeNode? node)
+    private TKey GetIDKey(CoreRow row)
+    {
+        if(IDColumn is not null)
+        {
+            return row.Get<TKey>(IDColumn.Property);
+        }
+        else if(IDKey is not null)
+        {
+            return IDKey(row);
+        }
+        else
+        {
+            return NullKey;
+        }
+    }
+    private TKey GetParentKey(CoreRow row)
+    {
+        if(ParentColumn is not null)
+        {
+            return row.Get<TKey>(ParentColumn.Property);
+        }
+        else if(ParentKey is not null)
+        {
+            return ParentKey(row);
+        }
+        else
+        {
+            return NullKey;
+        }
+    }
+
+    private void SetParent(T obj, TKey key)
+    {
+        if(ParentColumn is not null)
+        {
+            ParentColumn.PropertyDefinition.Setter()(obj, key);
+        }
+        else if(SetParentKey is not null)
+        {
+            SetParentKey(obj, key);
+        }
+    }
+
+    private CoreRow? GetRow(CoreTreeNode<TKey>? node)
     {
         return MapRow(node?.Row);
     }
@@ -892,7 +961,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         return Parent.Data.Rows[row.Index];
     }
 
-    private CoreTreeNode? GetNode(CoreRow row)
+    private CoreTreeNode<TKey>? GetNode(CoreRow row)
     {
         if (_innerTable is null || row.Index < 0 || row.Index >= _innerTable.Rows.Count) return null;
 
@@ -903,7 +972,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     public CoreRow[] GetVisibleRows()
     {
-        return _tree.View?.Nodes.Select(x => MapRow((x.Item as CoreTreeNode)?.Row)).NotNull().ToArray() ?? new CoreRow[] { };
+        return _tree.View?.Nodes.Select(x => MapRow((x.Item as CoreTreeNode<TKey>)?.Row)).NotNull().ToArray() ?? new CoreRow[] { };
     }
 
     #endregion
@@ -967,15 +1036,15 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         }
     }
 
-    public class TemplateColumnSelector(DynamicGridTreeUIComponent<T> parent, Func<CoreRow, FrameworkElement?> dataTemplate) : DataTemplateSelector
+    public class TemplateColumnSelector(DynamicGridTreeUIComponent<T, TKey> parent, Func<CoreRow, FrameworkElement?> dataTemplate) : DataTemplateSelector
     {
         public Func<CoreRow, FrameworkElement?> DataTemplate { get; init; } = dataTemplate;
 
-        public DynamicGridTreeUIComponent<T> Parent { get; init; } = parent;
+        public DynamicGridTreeUIComponent<T, TKey> Parent { get; init; } = parent;
 
         public override DataTemplate? SelectTemplate(object item, DependencyObject container)
         {
-            if (item is not CoreTreeNode node) return null;
+            if (item is not CoreTreeNode<TKey> node) return null;
 
             var row = Parent.MapRow(node.Row);
 
@@ -1350,7 +1419,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
     private object? CalculateSummaryData(IDynamicGridSummary summary, DynamicColumnBase column)
     {
         var nodes = _tree.View is not null
-            ? _tree.View.Nodes.Select(x => x.Item as CoreTreeNode).NotNull()
+            ? _tree.View.Nodes.Select(x => x.Item as CoreTreeNode<TKey>).NotNull()
             : Nodes.Nodes;
         if(summary is DynamicGridCountSummary count)
         {
@@ -1427,7 +1496,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     #region Refresh
 
-    public CoreTreeNodes Nodes { get; set; }
+    public CoreTreeNodes<TKey> Nodes { get; set; }
 
     private CoreTable? _innerTable;
     private bool _invalidating = false;
@@ -1444,7 +1513,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     public void RefreshData(CoreTable data)
     {
-        var nodes = new CoreTreeNodes();
+        var nodes = new CoreTreeNodes<TKey>(NullKey);
 
         _innerTable = new CoreTable();
         _innerTable.LoadColumns(data.Columns);
@@ -1467,9 +1536,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
             ProcessRow(newRow, row);
             _innerTable.Rows.Add(newRow);
 
-            var _id = row.Get<Guid>(IDColumn.Property);
-            var _parent = row.Get<Guid>(ParentColumn.Property);
-            nodes.Add(_id, _parent, newRow);
+            nodes.Add(GetIDKey(row), GetParentKey(row), newRow);
         }
         nodes.ColumnChanged += Nodes_ColumnChanged;
         Nodes = nodes;
@@ -1498,9 +1565,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
             ProcessRow(newRow, row);
             _innerTable.Rows.Add(newRow);
 
-            var _id = row.Get<Guid>(IDColumn.Property);
-            var _parent = row.Get<Guid>(ParentColumn.Property);
-            Nodes.Add(_id, _parent, newRow);
+            Nodes.Add(GetIDKey(row), GetParentKey(row), newRow);
         }
         CalculateSummaries();
 
@@ -1584,7 +1649,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         }
     }
 
-    public void UpdateRow(CoreRow row, CoreTreeNode dataRow)
+    public void UpdateRow(CoreRow row, CoreTreeNode<TKey> dataRow)
     {
         foreach(var (key, value) in row)
         {
@@ -1649,9 +1714,9 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
         public CoreRow Row { get; set; }
 
-        public CoreTreeNode? Node { get; set; }
+        public CoreTreeNode<TKey>? Node { get; set; }
 
-        public DirectEditingObject(T obj, CoreRow row, CoreTreeNode? node)
+        public DirectEditingObject(T obj, CoreRow row, CoreTreeNode<TKey>? node)
         {
             Object = obj;
             Row = row;
@@ -1690,7 +1755,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
             MessageWindow.ShowError($"Error saving {typeof(T)}", e);
         }
     }
-    private void UpdateData(CoreTreeNode node, int columnIndex)
+    private void UpdateData(CoreTreeNode<TKey> node, int columnIndex)
     {
         if (GetColumn(columnIndex) is DynamicGridColumn gridcol)
         {
@@ -1741,7 +1806,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
         bChanged = false;
     }
 
-    private void Nodes_ColumnChanged(CoreTreeNode node, string column)
+    private void Nodes_ColumnChanged(CoreTreeNode<TKey> node, string column)
     {
         if (_invalidating) return;
 
@@ -1859,7 +1924,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
 
     private void RowDragDropController_DragStart(object? sender, TreeGridRowDragStartEventArgs e)
     {
-        var rows = e.DraggingNodes.Select(node => MapRow((node.Item as CoreTreeNode)?.Row)).NotNull().ToArray();
+        var rows = e.DraggingNodes.Select(node => MapRow((node.Item as CoreTreeNode<TKey>)?.Row)).NotNull().ToArray();
         Parent.DragStart(sender, rows);
     }