6 Commits aa805a7640 ... 12c9d29564

Author SHA1 Message Date
  frogsoftware 12c9d29564 Merge remote-tracking branch 'origin/kenric' into frank 1 year ago
  Kenric Nugteren 1586c694cf Fixes to PosterEngine for multiple timberline things 1 year ago
  frogsoftware d84d92266e Fixed Bug with CoreTreeNode numbering 1 year ago
  Frank van den Bos e3890f3f58 Renamed and Moved InABox.Mobile.Shared.DynamicTreeNodes to InABox.Core.CoreTreeNodes 1 year ago
  Frank van den Bos 69f1d2682f Added Automatic Numbering to DynamicTreeNodes 1 year ago
  Kenric Nugteren 68a48f755c Improved exception handling for Posting 1 year ago

+ 47 - 22
InABox.Mobile/InABox.Mobile.Shared/DynamicTreeNodes.cs → InABox.Core/CoreTreeNodes.cs

@@ -4,17 +4,15 @@ using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Linq;
 using System.Linq.Expressions;
-using InABox.Core;
-using Xamarin.Forms;
 
-namespace InABox.Mobile
+namespace InABox.Core
 {
-    public class DynamicTreeNode : INotifyPropertyChanged
+    public class CoreTreeNode : INotifyPropertyChanged
     {
 
-        private DynamicTreeNodes _owner;
+        private CoreTreeNodes _owner;
         
-        public ObservableCollection<DynamicTreeNode> Children => _owner.GetChilden(_id);
+        public ObservableCollection<CoreTreeNode> Children => _owner.GetChilden(_id);
         
         private Guid _id;
         public Guid ID 
@@ -49,10 +47,10 @@ namespace InABox.Mobile
             }
         }
    
-        private ImageSource? _image;
-        public ImageSource? Image
+        private object _image;
+        public object Image
         {
-            get { return _image; }
+            get => _image;
             set
             {
                 _image = value;
@@ -67,40 +65,67 @@ namespace InABox.Mobile
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
         }
 
-        public DynamicTreeNode(DynamicTreeNodes owner)
+        public CoreTreeNode(CoreTreeNodes owner)
         {
             _owner = owner;
             _description = "";
         }
 
-        public DynamicTreeNode(DynamicTreeNodes owner, Guid id, Guid parent) : this(owner)
+        public CoreTreeNode(CoreTreeNodes owner, Guid id, Guid parent) : this(owner)
         {
             _id = id;
             _parent = parent;
         }
+
+        public CoreTreeNode? GetParent() => _owner[_parent];
+
+        public int Index()
+        {
+            var parent = GetParent();
+            return parent != null
+                ? parent.Children.IndexOf(this) + 1
+                : _owner.Nodes.IndexOf(this) + 1;
+        }
+
+        public String Number
+        {
+            get
+            {
+                String result = Index().ToString();
+                var parent = GetParent();
+                while (parent != null)
+                {
+                    int index = parent.Index();
+                    result = $"{index}.{result}";
+                    parent = parent.GetParent();
+                }
+                return result;
+            }
+        }
+        
     }
 
-    public class DynamicTreeNodes 
+    public class CoreTreeNodes 
     {
 
-        private List<DynamicTreeNode> _nodes;
-        public ObservableCollection<DynamicTreeNode> Nodes => new ObservableCollection<DynamicTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
+        private List<CoreTreeNode> _nodes;
+        public ObservableCollection<CoreTreeNode> Nodes => new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
         
-        public DynamicTreeNode? this[Guid id] => _nodes.FirstOrDefault(x => x.ID == id);
+        public CoreTreeNode? this[Guid id] => _nodes.FirstOrDefault(x => x.ID == id);
         
-        public DynamicTreeNodes()
+        public CoreTreeNodes()
         {
-            _nodes = new List<DynamicTreeNode>();
+            _nodes = new List<CoreTreeNode>();
         }
 
-        public DynamicTreeNode Add(Guid id, Guid parent)
+        public CoreTreeNode Add(Guid id, Guid parent)
         {
-            var node = new DynamicTreeNode(this, id, parent);
+            var node = new CoreTreeNode(this, id, parent);
             _nodes.Add(node);
             return node;
         }
         
-        public DynamicTreeNode Find(Guid id) => _nodes.FirstOrDefault(x => x.ID == id);
+        public CoreTreeNode Find(Guid id) => _nodes.FirstOrDefault(x => x.ID == id);
 
         public void GetChildren(List<Guid> nodes, Guid id)
         {
@@ -110,10 +135,10 @@ namespace InABox.Mobile
                 GetChildren(nodes, child.ID);
         }
         
-        public ObservableCollection<DynamicTreeNode> GetChilden(Guid id)
+        public ObservableCollection<CoreTreeNode> GetChilden(Guid id)
         {
             
-            return new ObservableCollection<DynamicTreeNode>(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id)));
+            return new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id)));
         }
 
         public void Load<T>(CoreTable table, Expression<Func<T, Guid>> id, Expression<Func<T, Guid>> parentid, Expression<Func<T, String>> description)

+ 18 - 12
InABox.Core/Postable/PosterEngine.cs

@@ -103,6 +103,16 @@ namespace InABox.Core
         /// <param name="model"></param>
         public abstract void AfterPost(IDataModel<TPostable> model);
 
+        private static void SetFailed(IList<TPostable> entities)
+        {
+            foreach (var post in entities)
+            {
+                post.PostedStatus = PostedStatus.PostFailed;
+                post.PostedNote = "Post failed.";
+            }
+            new Client<TPostable>().Save(entities, "Post failed by user.");
+        }
+
         public bool Process(IDataModel<TPostable> model)
         {
             if (!BeforePost(model))
@@ -144,26 +154,22 @@ namespace InABox.Core
                 }
                 else
                 {
-                    foreach (var post in entities)
-                    {
-                        post.PostedStatus = PostedStatus.PostFailed;
-                        post.PostedNote = "Post failed.";
-                    }
-                    new Client<TPostable>().Save(entities, "Post failed by user.");
+                    SetFailed(entities);
                 }
                 return success;
             }
+            catch (PostCancelledException)
+            {
+                var entities = data.ToObjects<TPostable>().ToList();
+                SetFailed(entities);
+                throw;
+            }
             catch(Exception e)
             {
                 Logger.Send(LogType.Error, "", $"Post Failed: {CoreUtils.FormatException(e)}");
 
                 var entities = data.ToObjects<TPostable>().ToList();
-                foreach (var post in entities)
-                {
-                    post.PostedStatus = PostedStatus.PostFailed;
-                    post.PostedNote = e.Message;
-                }
-                new Client<TPostable>().Save(entities, "Post failed by user.");
+                SetFailed(entities);
                 throw;
             }
         }

+ 18 - 3
InABox.Core/Postable/PosterUtils.cs

@@ -16,6 +16,8 @@ namespace InABox.Core
         {
             public Type Engine { get; set; }
 
+            public Type Entity { get; set; }
+
             public Type Poster { get; set; }
         }
 
@@ -44,6 +46,7 @@ namespace InABox.Core
             ).Select(x => new EngineType
             {
                 Engine = x,
+                Entity = x.GetInterfaceDefinition(typeof(IPosterEngine<,,>))!.GenericTypeArguments[0],
                 Poster = x.GetInterfaceDefinition(typeof(IPosterEngine<,,>))!.GenericTypeArguments[1].GetGenericTypeDefinition()
             }).ToArray();
             return _posterEngines;
@@ -129,14 +132,26 @@ namespace InABox.Core
             }
             var poster = GetPosters()?.FirstOrDefault(x => x.EntityName() == settings.PosterType)!;
 
-            return (GetPosterEngines().FirstOrDefault(x => poster.HasInterface(x.Poster))?.Engine
-                ?? throw new Exception("No poster for the given settings.")).MakeGenericType(typeof(T));
+            var engines = GetPosterEngines().Where(x => poster.HasInterface(x.Poster)).ToList();
+            if (!engines.Any())
+            {
+                throw new Exception("No poster for the given settings.");
+            }
+            else if(engines.Count == 1)
+            {
+                return engines[0].Engine.MakeGenericType(typeof(T));
+            }
+            else
+            {
+                return engines.Single(x => x.Entity == typeof(T)).Engine.MakeGenericType(typeof(T));
+            }
         }
 
         public static IPosterEngine<T> CreateEngine<T>()
             where T : Entity, IPostable, IRemotable, IPersistent, new()
         {
-            return (Activator.CreateInstance(GetEngine<T>()) as IPosterEngine<T>)!;
+            var engine = GetEngine<T>();
+            return (Activator.CreateInstance(engine) as IPosterEngine<T>)!;
         }
 
         /// <summary>

+ 8 - 0
InABox.Mobile/InABox.Mobile.Shared/Components/MobileDateSelector/MobileDateButton.xaml

@@ -18,6 +18,14 @@
                 BackgroundColor="{TemplateBinding ButtonColor}"
                 BorderColor="{TemplateBinding BorderColor}"
                 Clicked="_frame_OnClicked">
+                <local:MobileCard.Triggers>
+                    <DataTrigger TargetType="local:MobileCard" Binding="{TemplateBinding IsEnabled}" Value="False">
+                        <Setter Property="BackgroundColor" Value="Silver" />
+                    </DataTrigger>
+                    <DataTrigger TargetType="local:MobileCard" Binding="{TemplateBinding IsEnabled}" Value="False">
+                        <Setter Property="BorderColor" Value="Gray" />
+                    </DataTrigger>
+                </local:MobileCard.Triggers>
                 
                 <material:MaterialLabel 
                     x:Name="_label" 

+ 12 - 0
InABox.Mobile/InABox.Mobile.Shared/Components/MobileGrid/MobileGrid.xaml.cs

@@ -129,6 +129,18 @@ namespace InABox.Mobile
             }
             return false;
         }
+
+        public double RowHeight
+        {
+            get => Grid.RowHeight;
+            set => Grid.RowHeight = value;
+        }
+
+        public void ClearSelection()
+        {
+            Grid.SelectedItem = null;
+            Grid.SelectedItems.Clear();
+        }
         
         public MobileGrid()
         {

+ 9 - 1
InABox.Mobile/InABox.Mobile.Shared/Components/MobileTimeSelector/MobileTimeButton.xaml

@@ -21,8 +21,16 @@
                 BackgroundColor="{TemplateBinding ButtonColor}"
                 BorderColor="{TemplateBinding BorderColor}"
                 Clicked="_frame_OnClicked">
+                <local:MobileCard.Triggers>
+                    <DataTrigger TargetType="local:MobileCard" Binding="{TemplateBinding IsEnabled}" Value="False">
+                        <Setter Property="BackgroundColor" Value="Silver" />
+                    </DataTrigger>
+                    <DataTrigger TargetType="local:MobileCard" Binding="{TemplateBinding IsEnabled}" Value="False">
+                        <Setter Property="BorderColor" Value="Gray" />
+                    </DataTrigger>
+                </local:MobileCard.Triggers>
                 
-                    <material:MaterialLabel 
+                <material:MaterialLabel 
                         x:Name="_label" 
                         Text="{TemplateBinding Time, Converter={StaticResource TimeSpanFormatter}}" 
                         VerticalOptions="CenterAndExpand" 

+ 4 - 12
InABox.Mobile/InABox.Mobile.Shared/Converters/BooleanToColorConverter.cs

@@ -4,23 +4,15 @@ using Xamarin.Forms;
 
 namespace InABox.Mobile
 {
-    public class BooleanToColorConverter: BindableObject, IValueConverter
+    public class BooleanToColorConverter: UtilityConverter<bool,Color>
     {
         
         public Color TrueColor { get; set; }
         public Color FalseColor { get; set; }
-        
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+
+        protected override Color Convert(bool value)
         {
-            if (value is bool bValue)
-                return bValue ? TrueColor : FalseColor;
-            return FalseColor;
+            return value ? TrueColor : FalseColor;
         }
-    
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        } 
     }
-    
 }

+ 20 - 0
InABox.Mobile/InABox.Mobile.Shared/Converters/DateTimeToColorConverter.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+using InABox.Core;
+using Xamarin.Forms;
+
+namespace InABox.Mobile
+{
+    public class DateTimeToColorConverter: UtilityConverter<DateTime,Color>
+    {
+        
+        public Color EmptyColor { get; set; }
+        public Color FilledColor { get; set; }
+
+        protected override Color Convert(DateTime value)
+        {
+            return value.IsEmpty() ? EmptyColor : FilledColor;
+        }
+        
+    }
+}

+ 5 - 3
InABox.Mobile/InABox.Mobile.Shared/Converters/TimeSpanToStringConverter.cs → InABox.Mobile/InABox.Mobile.Shared/Converters/FormatConverter.cs

@@ -10,9 +10,11 @@ namespace InABox.Mobile
 
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            return String.IsNullOrWhiteSpace(Format)
-                ? value
-                : String.Format($"{{0:{Format}}}", value);
+            var fmt = String.IsNullOrWhiteSpace(Format)
+                ? "{0}"
+                : $"{{0:{Format}}}";
+            
+            return String.Format($"{fmt}", value);
         }
 
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

+ 11 - 5
InABox.Mobile/InABox.Mobile.Shared/Converters/StringToBooleanConverter.cs

@@ -5,17 +5,23 @@ using Xamarin.Forms;
 
 namespace InABox.Mobile
 {
-    public class StringToBooleanConverter : IValueConverter
+    public class StringToBooleanConverter : UtilityConverter<String,bool>
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public bool HasValue { get; set; }
+
+        protected override bool Convert(string value)
         {
-            return !String.IsNullOrWhiteSpace(value as String);
+            var empty = String.IsNullOrWhiteSpace(value);
+            return HasValue 
+                ? !empty 
+                : empty;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public StringToBooleanConverter()
         {
-            throw new NotImplementedException();
+            HasValue = true;
         }
+        
     }
     
     public class StringWithDefaultValueConverter : IValueConverter

+ 151 - 133
inabox.wpf/DynamicGrid/DynamicTreeView.cs

@@ -4,138 +4,134 @@ using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Linq;
 using System.Linq.Expressions;
-using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using InABox.Core;
 using InABox.WPF;
-using NPOI.OpenXmlFormats.Dml.Chart;
-using NPOI.OpenXmlFormats.Dml.Spreadsheet;
 using Syncfusion.UI.Xaml.TreeGrid;
-using Syncfusion.Windows.Tools.Controls;
 
 namespace InABox.DynamicGrid
 {
-    public class DynamicTreeNode : INotifyPropertyChanged
-    {
-
-        private DynamicTreeNodes _owner;
-        
-        public ObservableCollection<DynamicTreeNode> Children => _owner.GetChilden(_id);
-        
-        private Guid _id;
-        public Guid ID 
-        {
-            get { return _id; }
-            set
-            {
-                _id = value;
-                RaisedOnPropertyChanged("ID");
-            }
-        }
-        
-        private Guid _parent;
-        public Guid Parent 
-        {
-            get { return _parent; }
-            set
-            {
-                _parent = value;
-                RaisedOnPropertyChanged("Parent");
-            }
-        }
-
-        private string _description;
-        public string Description
-        {
-            get { return _description; }
-            set
-            {
-                _description = value;
-                RaisedOnPropertyChanged("Description");
-            }
-        }
-   
-        private ImageSource? _image;
-        public ImageSource? Image
-        {
-            get { return _image; }
-            set
-            {
-                _image = value;
-                RaisedOnPropertyChanged("Image");
-            }
-        }
-
-        public event PropertyChangedEventHandler? PropertyChanged;
-
-        public void RaisedOnPropertyChanged(string propertyName)
-        {
-            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
-        }
-
-        public DynamicTreeNode(DynamicTreeNodes owner)
-        {
-            _owner = owner;
-            _description = "";
-        }
-
-        public DynamicTreeNode(DynamicTreeNodes owner, Guid id, Guid parent) : this(owner)
-        {
-            _id = id;
-            _parent = parent;
-        }
-    }
-
-    public class DynamicTreeNodes 
-    {
-
-        private List<DynamicTreeNode> _nodes;
-        public ObservableCollection<DynamicTreeNode> Nodes => new ObservableCollection<DynamicTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
-        
-        public DynamicTreeNode? this[Guid id] => _nodes.FirstOrDefault(x => x.ID == id);
-        
-        public DynamicTreeNodes()
-        {
-            _nodes = new List<DynamicTreeNode>();
-        }
-
-        public DynamicTreeNode Add(Guid id, Guid parent)
-        {
-            var node = new DynamicTreeNode(this, id, parent);
-            _nodes.Add(node);
-            return node;
-        }
-
-        public void GetChildren(List<Guid> nodes, Guid id)
-        {
-            nodes.Add(id);
-            var children = GetChilden(id);
-            foreach (var child in children)
-                GetChildren(nodes, child.ID);
-        }
-        
-        public ObservableCollection<DynamicTreeNode> GetChilden(Guid id)
-        {
-            
-            return new ObservableCollection<DynamicTreeNode>(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id)));
-        }
-
-        public void Load<T>(CoreTable table, Expression<Func<T, Guid>> id, Expression<Func<T, Guid>> parentid, Expression<Func<T, String>> description)
-        {
-            _nodes.Clear();
-            foreach (var row in table.Rows)
-            {
-                Guid _id = row.Get<T, Guid>(id);
-                Guid _parent = row.Get<T, Guid>(parentid);
-                String _description = row.Get<T, String>(description);
-                Add(_id, _parent).Description = _description;
-            }
-        }
-        
-    }
+    // public class CoreTreeNode : INotifyPropertyChanged
+    // {
+    //
+    //     private CoreTreeNodes _owner;
+    //     
+    //     public ObservableCollection<CoreTreeNode> Children => _owner.GetChilden(_id);
+    //     
+    //     private Guid _id;
+    //     public Guid ID 
+    //     {
+    //         get { return _id; }
+    //         set
+    //         {
+    //             _id = value;
+    //             RaisedOnPropertyChanged("ID");
+    //         }
+    //     }
+    //     
+    //     private Guid _parent;
+    //     public Guid Parent 
+    //     {
+    //         get { return _parent; }
+    //         set
+    //         {
+    //             _parent = value;
+    //             RaisedOnPropertyChanged("Parent");
+    //         }
+    //     }
+    //
+    //     private string _description;
+    //     public string Description
+    //     {
+    //         get { return _description; }
+    //         set
+    //         {
+    //             _description = value;
+    //             RaisedOnPropertyChanged("Description");
+    //         }
+    //     }
+    //
+    //     private ImageSource? _image;
+    //     public ImageSource? Image
+    //     {
+    //         get { return _image; }
+    //         set
+    //         {
+    //             _image = value;
+    //             RaisedOnPropertyChanged("Image");
+    //         }
+    //     }
+    //
+    //     public event PropertyChangedEventHandler? PropertyChanged;
+    //
+    //     public void RaisedOnPropertyChanged(string propertyName)
+    //     {
+    //         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+    //     }
+    //
+    //     public CoreTreeNode(CoreTreeNodes owner)
+    //     {
+    //         _owner = owner;
+    //         _description = "";
+    //     }
+    //
+    //     public CoreTreeNode(CoreTreeNodes owner, Guid id, Guid parent) : this(owner)
+    //     {
+    //         _id = id;
+    //         _parent = parent;
+    //     }
+    // }
+    //
+    // public class CoreTreeNodes 
+    // {
+    //
+    //     private List<CoreTreeNode> _nodes;
+    //     public ObservableCollection<CoreTreeNode> Nodes => new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent == Guid.Empty));
+    //     
+    //     public CoreTreeNode? this[Guid id] => _nodes.FirstOrDefault(x => x.ID == id);
+    //     
+    //     public CoreTreeNodes()
+    //     {
+    //         _nodes = new List<CoreTreeNode>();
+    //     }
+    //
+    //     public CoreTreeNode Add(Guid id, Guid parent)
+    //     {
+    //         var node = new CoreTreeNode(this, id, parent);
+    //         _nodes.Add(node);
+    //         return node;
+    //     }
+    //
+    //     public void GetChildren(List<Guid> nodes, Guid id)
+    //     {
+    //         nodes.Add(id);
+    //         var children = GetChilden(id);
+    //         foreach (var child in children)
+    //             GetChildren(nodes, child.ID);
+    //     }
+    //     
+    //     public ObservableCollection<CoreTreeNode> GetChilden(Guid id)
+    //     {
+    //         
+    //         return new ObservableCollection<CoreTreeNode>(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id)));
+    //     }
+    //
+    //     public void Load<T>(CoreTable table, Expression<Func<T, Guid>> id, Expression<Func<T, Guid>> parentid, Expression<Func<T, String>> description)
+    //     {
+    //         _nodes.Clear();
+    //         foreach (var row in table.Rows)
+    //         {
+    //             Guid _id = row.Get<T, Guid>(id);
+    //             Guid _parent = row.Get<T, Guid>(parentid);
+    //             String _description = row.Get<T, String>(description);
+    //             Add(_id, _parent).Description = _description;
+    //         }
+    //     }
+    //     
+    // }
     
     public enum DynamicTreeOption
     {
@@ -144,9 +140,9 @@ namespace InABox.DynamicGrid
         Delete
     }
 
-    public delegate void OnSelectItem(DynamicTreeNode node);
+    public delegate void OnSelectItem(CoreTreeNode node);
 
-    public delegate void OnContextMenuOpening(DynamicTreeNode node, ContextMenu menu);
+    public delegate void OnContextMenuOpening(CoreTreeNode node, ContextMenu menu);
     
     public abstract class DynamicTreeView<T> : ContentControl where T : BaseObject, new()
     {
@@ -192,6 +188,17 @@ namespace InABox.DynamicGrid
             }
         }
 
+        private bool _shownumbers = false;
+        public bool ShowNumbers
+        {
+            get => _shownumbers;
+            set
+            {
+                _shownumbers = value;
+                _tree.Columns[1].Width = value ? 50 : 0;
+            } 
+        }
+
         /*public double RowHeight
         {
             get => _tree.RowHeight;
@@ -216,12 +223,12 @@ 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 CoreTreeNode)!);
             _tree.AllowSelectionOnExpanderClick = false;
 
             _menu = new ContextMenu();
             var additem = new MenuItem() { Header = "Add Child Folder" };
-            additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as DynamicTreeNode)!.ID, true); };
+            additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as CoreTreeNode)!.ID, true); };
             _menu.Items.Add(additem);
 
             _tree.ContextMenuOpening += _tree_ContextMenuOpening;
@@ -233,11 +240,21 @@ namespace InABox.DynamicGrid
             _tree.RowStyle = cellStyle;            
             
             _tree.SelectionBackground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0x11, 0x9E, 0xD9));
+            
             _tree.Columns.Add(new TreeGridTextColumn()
                 {
                     MappingName = "Description"
                 }
             );
+            
+            _tree.Columns.Add(new TreeGridTextColumn()
+                {
+                    MappingName = "Number",
+                    Width = _shownumbers ? 50 : 0,
+                    TextAlignment = TextAlignment.Right
+                }
+            );
+            
             _tree.ColumnSizer = TreeColumnSizer.Star;
             _tree.RowHeight = 30D;
             _tree.SetValue(Grid.RowProperty, 0);
@@ -276,7 +293,7 @@ namespace InABox.DynamicGrid
 
         #region Public Interface
 
-        public void AddItem(DynamicTreeNode? parentNode = null, bool edit = true)
+        public void AddItem(CoreTreeNode? parentNode = null, bool edit = true)
         {
             var id = parentNode?.ID ?? Guid.Empty;
             DoAddItem(id, edit);
@@ -290,7 +307,7 @@ namespace InABox.DynamicGrid
             _menu.Items.Clear();
             if (OnContextMenuOpening is not null)
             {
-                OnContextMenuOpening.Invoke((_tree.SelectedItem as DynamicTreeNode)!, _menu);
+                OnContextMenuOpening.Invoke((_tree.SelectedItem as CoreTreeNode)!, _menu);
                 if(_menu.Items.Count == 0)
                 {
                     e.Handled = true;
@@ -298,7 +315,7 @@ namespace InABox.DynamicGrid
             }
             else
             {
-                _menu.AddItem("Add Item", null, (_tree.SelectedItem as DynamicTreeNode)!.ID, (id) => DoAddItem(id,true));
+                _menu.AddItem("Add Item", null, (_tree.SelectedItem as CoreTreeNode)!.ID, (id) => DoAddItem(id,true));
             }
         }
 
@@ -412,7 +429,7 @@ namespace InABox.DynamicGrid
 
         private void EditItem(Button button)
         {
-            var node = _tree.SelectedItem as DynamicTreeNode;
+            var node = _tree.SelectedItem as CoreTreeNode;
             if (node == null)
             {
                 MessageBox.Show("Please Select an item to edit!");
@@ -428,7 +445,7 @@ namespace InABox.DynamicGrid
 
         private  void DeleteItem(Button button)
         {
-            var node = _tree.SelectedItem as DynamicTreeNode;
+            var node = _tree.SelectedItem as CoreTreeNode;
             if (node == null)
             {
                 MessageBox.Show("Please Select an item to edit!");
@@ -441,13 +458,13 @@ namespace InABox.DynamicGrid
             }
         }
         
-        public DynamicTreeNodes Nodes { get; set; }
+        public CoreTreeNodes Nodes { get; set; }
 
         protected abstract void DoRefresh(Action<CoreTable?, Exception?> action);
 
         private void AfterRefresh()
         {
-            var nodes = new DynamicTreeNodes();
+            var nodes = new CoreTreeNodes();
             foreach (var row in Data.Rows)
             {
                 var _id = row.Get(ID);
@@ -462,6 +479,7 @@ namespace InABox.DynamicGrid
 
         public void Refresh()
         {
+            _tree.ItemsSource = null;
             DoRefresh((table, exception) =>
             {
                 if(exception != null)