Переглянути джерело

Added ability to add Buttons on the right-hand side of a grid
Added EventSuppressor class to help with Dependency Property nightmares
Added Utility functions to merge and resize Bitmaps

Frank van den Bos 2 роки тому
батько
коміт
42e770f411

+ 7 - 0
InABox.Configuration/IConfiguration.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 
 namespace InABox.Configuration
 {
+    
     public interface IConfiguration<T>
     {
         string[] Sections();
@@ -48,4 +49,10 @@ namespace InABox.Configuration
     public interface IConfigurationSettings
     {
     }
+    
+    public delegate T LoadSettings<T>(object sender) where T : IConfigurationSettings;
+    
+    public delegate void SaveSettings<T>(object sender, T properties) where T : IConfigurationSettings;
+
+
 }

+ 31 - 1
InABox.Core/CoreUtils.cs

@@ -2522,7 +2522,37 @@ namespace InABox.Core
             return result;
         }
         
+        public static string Codify(string? text)
+        {
+
+            char[] numbers = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+            
+            if (string.IsNullOrWhiteSpace(text))
+                return "??";
+
+            var result = "";
+            var comps = text.ToUpper().Split(' ');
+            foreach (var comp in comps)
+            {
+                List<char> chars = new List<char>();
+                foreach (char c in comp.ToCharArray())
+                {
+                    chars.Add(c);
+                    if (!numbers.Contains(c))
+                        break;
+                }
+                var comb = new String(chars.ToArray());
+                result += comb;
+            }
+            return string.IsNullOrWhiteSpace(result) ? "??" : result;
+        }
         
-        
+
+        public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
+        {
+            int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
+            return dt.AddDays(-1 * diff).Date;
+        }
+
     }
 }

+ 8 - 1
InABox.Core/Editors/EmbeddedListEditor.cs

@@ -2,12 +2,19 @@ using System;
 
 namespace InABox.Core
 {
+
+    public delegate void EmbeddedListEditorCreateButtons(object sender);
+        
     public class EmbeddedListEditor : BaseEditor
     {
         public Type DataType;
         
         public String Label { get; set; }
 
+        public event EmbeddedListEditorCreateButtons OnCreateButtons;
+
+        public void CreateButtons(object sender) => OnCreateButtons?.Invoke(sender);
+
         public EmbeddedListEditor(Type dataType, String label = "Edit")
         {
             if (!typeof(BaseObject).IsAssignableFrom(dataType))
@@ -17,6 +24,6 @@ namespace InABox.Core
         }
 
         protected override BaseEditor DoClone() => new EmbeddedListEditor(DataType, Label);
-
+        
     }
 }

+ 51 - 0
InABox.Core/EventSuppressor.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace InABox.Core
+{
+    
+    public class EventSuppressor : IDisposable
+    {
+        private static Dictionary<Enum, int> _cache = new Dictionary<Enum, int>();
+
+        private Enum[] _scopes;
+
+        public static ImmutableDictionary<Enum, int> Cache() => _cache.ToImmutableDictionary();
+        
+        public EventSuppressor(params Enum[] scopes)
+        {
+            _scopes = scopes;
+            foreach (var scope in scopes)
+            {
+                _cache.TryGetValue(scope, out int count);
+                _cache[scope] = count + 1;
+            }
+        }
+        
+        public void Dispose()
+        {
+            foreach (var scope in _scopes)
+            {
+                if (_cache.TryGetValue(scope, out int count))
+                {
+                    if (count <= 1)
+                        _cache.Remove(scope);
+                    else
+                        _cache[scope] = count - 1;
+                }
+            }
+        }
+
+        public static bool IsSet(Enum scope) => _cache.TryGetValue(scope, out int count) && (count > 0);
+
+        public static EventSuppressor All<T>() where T : Enum
+        {
+            var scopes = Enum.GetValues(typeof(T)).Cast<Enum>().ToArray();
+            return new EventSuppressor(scopes);
+        }
+        
+    }
+}

+ 1 - 0
InABox.Core/InABox.Core.csproj

@@ -31,6 +31,7 @@
         <PackageReference Include="Inflector.NetStandard" Version="1.2.2" />
         <PackageReference Include="PropertyChanged.Fody" Version="3.4.1" />
         <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+        <PackageReference Include="System.Collections.Immutable" Version="6.0.0" />
         <PackageReference Include="TextFieldParserStandard" Version="1.0.0" />
     </ItemGroup>
     <ItemGroup>

+ 5 - 3
InABox.DynamicGrid/BaseDynamicGrid.cs

@@ -5,6 +5,7 @@ using System.Linq.Expressions;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Media;
+using System.Windows.Media.Imaging;
 using InABox.Core;
 
 namespace InABox.DynamicGrid
@@ -55,6 +56,7 @@ namespace InABox.DynamicGrid
         public EntitySaveEvent? OnBeforeSave;
         public event OnPrintData? OnPrintData;
         public event OnAfterReloadEventHandler? OnAfterReload;
+        
         public event OnDefineFilter? OnDefineFilter;
         public abstract event OnFilterRecord? OnFilterRecord;
         public event OnCreateItem? OnCreateItem;
@@ -103,9 +105,9 @@ namespace InABox.DynamicGrid
 
         public abstract void AddVisualFilter(string column, string value);
 
-
-        //public event DefineFilter OnDefineFilter;
-
+        public abstract Button AddButton(string caption, BitmapImage? image, string? tooltip, Func<Button, CoreRow[], bool> action,
+            DynamicGridButtonPosition position = DynamicGridButtonPosition.Left);
+        
         public virtual double RowHeight { get; set; }
         public virtual double HeaderHeight { get; set; }
         public new virtual double FontSize { get; set; }

+ 10 - 5
InABox.DynamicGrid/DynamicCrossJoinGrid.cs

@@ -12,18 +12,23 @@ namespace InABox.DynamicGrid
         where TEntity : Entity, IRemotable, IPersistent, new()
         where TLeft : Entity
     {
-        public TLeft Left { get; set; }
-        public Expression<Func<TEntity, Guid>> LeftMapping { get; set; }
-        public Expression<Func<TLeft, Guid>> LeftProperty { get; set; }
+        
+        public TLeft? Left { get; set; }
+        public Expression<Func<TEntity, Guid>>? LeftMapping { get; init; }
+        public Expression<Func<TLeft, Guid>>? LeftProperty { get; init; }
+
+        public DynamicCrossJoinGrid()
+        {
+            Options.BeginUpdate().Clear().Add(DynamicGridOption.SelectColumns).EndUpdate();
+        }
 
         public DynamicCrossJoinGrid(TLeft left, Expression<Func<TEntity, Guid>> leftMapping, Expression<Func<TLeft, Guid>> leftProperty)
         {
             Left = left;
             LeftMapping = leftMapping;
             LeftProperty = leftProperty;
-
-            Options.BeginUpdate().Clear().Add(DynamicGridOption.FilterRows).Add(DynamicGridOption.SelectColumns).EndUpdate();
         }
+
         protected override void GenerateColumns(DynamicGridColumns columns)
         {
             base.GenerateColumns(columns);

+ 1 - 1
InABox.DynamicGrid/DynamicEntityFormGrid.cs

@@ -65,7 +65,7 @@ namespace InABox.DynamicGrid
                     SaveItem(form);
 
                     Refresh(false, true);
-                    OnChanged?.Invoke(this);
+                    DoChanged();
                 }
             }
         }

+ 54 - 30
InABox.DynamicGrid/DynamicGrid.cs

@@ -359,7 +359,8 @@ namespace InABox.DynamicGrid
         private readonly Button Print;
         private readonly Label PrintSpacer;
 
-        private readonly StackPanel Stack;
+        private readonly StackPanel LeftButtonStack;
+        private readonly StackPanel RightButtonStack;
 
         private readonly DynamicRowMovementColumn? up;
 
@@ -371,7 +372,9 @@ namespace InABox.DynamicGrid
 
         public event OnCellDoubleClick? OnCellDoubleClick;
 
-        public OnGridChanged? OnChanged;
+        public event OnGridChanged? OnChanged;
+
+        public void DoChanged() => OnChanged?.Invoke(this);
 
         public event EditorValueChangedHandler? OnEditorValueChanged;
 
@@ -540,30 +543,34 @@ namespace InABox.DynamicGrid
 
             ExportSpacer = new Label { Width = 5 };
 
-            Stack = new StackPanel();
-            Stack.Orientation = Orientation.Horizontal;
-            Stack.SetValue(DockPanel.DockProperty, Dock.Left);
+            LeftButtonStack = new StackPanel();
+            LeftButtonStack.Orientation = Orientation.Horizontal;
+            LeftButtonStack.SetValue(DockPanel.DockProperty, Dock.Left);
 
-            Stack.Children.Add(Help);
-            Stack.Children.Add(Add);
-            Stack.Children.Add(Edit);
+            LeftButtonStack.Children.Add(Help);
+            LeftButtonStack.Children.Add(Add);
+            LeftButtonStack.Children.Add(Edit);
             //Stack.Children.Add(MultiEdit);
-            Stack.Children.Add(EditSpacer);
+            LeftButtonStack.Children.Add(EditSpacer);
 
-            Stack.Children.Add(Print);
-            Stack.Children.Add(PrintSpacer);
+            LeftButtonStack.Children.Add(Print);
+            LeftButtonStack.Children.Add(PrintSpacer);
 
-            Stack.Children.Add(Cut);
-            Stack.Children.Add(Copy);
-            Stack.Children.Add(Paste);
-            Stack.Children.Add(ClipboardSpacer);
+            LeftButtonStack.Children.Add(Cut);
+            LeftButtonStack.Children.Add(Copy);
+            LeftButtonStack.Children.Add(Paste);
+            LeftButtonStack.Children.Add(ClipboardSpacer);
 
-            Stack.Children.Add(Export);
-            Stack.Children.Add(Import);
-            Stack.Children.Add(ExportSpacer);
+            LeftButtonStack.Children.Add(Export);
+            LeftButtonStack.Children.Add(Import);
+            LeftButtonStack.Children.Add(ExportSpacer);
+            
+            RightButtonStack = new StackPanel();
+            RightButtonStack.Orientation = Orientation.Horizontal;
+            RightButtonStack.SetValue(DockPanel.DockProperty, Dock.Right);
 
             Delete = CreateButton(Properties.Resources.delete.AsBitmapImage(Color.White));
-            Delete.Margin = new Thickness(0, 2, 0, 0);
+            Delete.Margin = new Thickness(2, 2, 0, 0);
             Delete.SetValue(DockPanel.DockProperty, Dock.Right);
             Delete.Click += Delete_Click;
 
@@ -580,8 +587,9 @@ namespace InABox.DynamicGrid
 
             Docker.SetValue(Grid.RowProperty, 2);
             Docker.SetValue(Grid.ColumnProperty, 0);
-            Docker.Children.Add(Stack);
+            Docker.Children.Add(LeftButtonStack);
             Docker.Children.Add(Delete);
+            Docker.Children.Add(RightButtonStack);
             Docker.Children.Add(Count);
 
 
@@ -1189,6 +1197,8 @@ namespace InABox.DynamicGrid
             var visualContainer = DataGrid.GetVisualContainer();
             var rowcolumnindex = visualContainer.PointToCellRowColumnIndex(e.GetPosition(visualContainer));
             var columnindex = DataGrid.ResolveToGridVisibleColumnIndex(rowcolumnindex.ColumnIndex);
+            if ((columnindex < 0) || (columnindex >= ColumnList.Count))
+                return;
             var column = ColumnList[columnindex] as DynamicActionColumn;
             var rowindex = rowcolumnindex.RowIndex - (Options.Contains(DynamicGridOption.FilterRows) ? 2 : 1);
             if (rowindex < 0)
@@ -1421,8 +1431,9 @@ namespace InABox.DynamicGrid
                         newcol.ImageWidth = DataGrid.RowHeight - 8;
                         newcol.ColumnSizer = GridLengthUnitType.None;
                         newcol.HeaderText = column.HeaderText;
+                        newcol.AllowSorting = false;
                         
-                        ApplyFilterStyle(newcol, true);
+                        ApplyFilterStyle(newcol, true, true);
                         
                         newcol.ShowToolTip = column.ToolTip != null;
                         
@@ -1759,7 +1770,7 @@ namespace InABox.DynamicGrid
                         : HorizontalAlignment.Right;
 
 
-                    ApplyFilterStyle(newcol,filtering);
+                    ApplyFilterStyle(newcol, filtering, false);
 
 
                     var headstyle = new Style(typeof(GridHeaderCellControl));
@@ -1828,7 +1839,7 @@ namespace InABox.DynamicGrid
             ResizeColumns(DataGrid, DataGrid.ActualWidth - 2, DataGrid.ActualHeight - 2);
         }
 
-        private void ApplyFilterStyle(GridColumn column, bool filtering)
+        private void ApplyFilterStyle(GridColumn column, bool filtering, bool isactioncolumn)
         {
             var filterstyle = new Style();
             if (filtering)
@@ -1839,7 +1850,9 @@ namespace InABox.DynamicGrid
                 column.FilterRowCondition = FilterRowCondition.Contains;
                 column.FilterRowOptionsVisibility = Visibility.Collapsed;
                 column.AllowBlankFilters = true;
-                column.AllowSorting = CanSort();
+                column.AllowSorting = isactioncolumn 
+                    ? false 
+                    : CanSort();
             }
             else
             {
@@ -2759,6 +2772,7 @@ namespace InABox.DynamicGrid
                 
                 var item = CreateItem();
 
+                // Yea, and this won't work, because we're actually usually showing the description of a linked item,
                 // Yea, and this won't work, because we're actually usually showing the description of a linked item,
                 // not the id of the link, and we need to set the ID to have it work properly :-(
 
@@ -3209,6 +3223,7 @@ namespace InABox.DynamicGrid
 
             if (!string.IsNullOrEmpty(text))
             {
+                button.MaxWidth = double.MaxValue;
                 var lbl = new Label();
                 lbl.Content = text;
                 lbl.VerticalAlignment = VerticalAlignment.Stretch;
@@ -3217,6 +3232,8 @@ namespace InABox.DynamicGrid
                 lbl.ToolTip = ToolTip;
                 stackPnl.Children.Add(lbl);
             }
+            else
+                button.MaxWidth = 30;
 
             button.Content = stackPnl;
             button.ToolTip = tooltip;
@@ -3238,21 +3255,28 @@ namespace InABox.DynamicGrid
         }
 
 
-        public Button AddButton(string caption, BitmapImage? image, string? tooltip, Func<Button, CoreRow[], bool> action)
+        public override Button AddButton(string caption, BitmapImage? image, string? tooltip, Func<Button, CoreRow[], bool> action, DynamicGridButtonPosition position = DynamicGridButtonPosition.Left)
         {
             var button = CreateButton(image, caption, tooltip);
-            button.Margin = new Thickness(bFirstButtonAdded && AnyButtonsVisible() ? /* 10 */ 0 : 0, 2, 2, 0);
-            button.Padding = new Thickness(5, 0, 5, 0);
+            button.Margin = position == DynamicGridButtonPosition.Right
+                ? new Thickness(2, 2, 0, 0)
+                : bFirstButtonAdded && AnyButtonsVisible() 
+                    ? new Thickness(0, 2, 0, 0)
+                    : new Thickness(0, 2, 2, 0);
+            button.Padding = !String.IsNullOrWhiteSpace(caption) ? new Thickness(5, 1, 5, 1) : new Thickness(1);
             button.Tag = action;
             button.Click += Button_Click;
-            Stack.Children.Add(button);
+            if (position == DynamicGridButtonPosition.Right)
+                RightButtonStack.Children.Add(button);
+            else
+                LeftButtonStack.Children.Add(button);
             bFirstButtonAdded = false;
             return button;
         }
 
-        public Button AddButton(string caption, BitmapImage? image, Func<Button, CoreRow[], bool> action)
+        public Button AddButton(string caption, BitmapImage? image, Func<Button, CoreRow[], bool> action, DynamicGridButtonPosition position = DynamicGridButtonPosition.Left)
         {
-            var result = AddButton(caption, image, null, action);
+            var result = AddButton(caption, image, null, action, position);
             return result;
         }
 

+ 1 - 0
InABox.DynamicGrid/DynamicItemsListGrid.cs

@@ -42,6 +42,7 @@ namespace InABox.DynamicGrid
         {
             var result = new CoreTable();
             result.LoadColumns(typeof(T));
+            
             result.LoadRows(Items);
             action.Invoke(result, null);
         }

+ 1 - 0
InABox.DynamicGrid/DynamicSplitPanel.cs

@@ -230,6 +230,7 @@ namespace InABox.DynamicGrid
                         SecondaryDetail == null ? new GridLength(0.0F, GridUnitType.Pixel) : new GridLength(1.0F, GridUnitType.Auto);
                     DetailGrid.RowDefinitions[2].Height =
                         SecondaryDetail == null ? new GridLength(0.0F, GridUnitType.Pixel) : new GridLength(1.0F, GridUnitType.Star);
+                    DetailSplitter.IsEnabled = SecondaryDetail != null;
                 }
             }
             catch (Exception e)

+ 6 - 3
InABox.DynamicGrid/Editors/EmbeddedListEditorControl.cs

@@ -38,15 +38,18 @@ namespace InABox.DynamicGrid
             var list = Serialization.Deserialize(listtype, _data) ?? (IList)Activator.CreateInstance(listtype)!;
             var gridtype = DynamicGridUtils.FindDynamicGrid(typeof(DynamicItemsListGrid<>), DataType);
             
-            var grid = Activator.CreateInstance(gridtype, new object[] { list })!;
-            ((IDynamicGrid)grid).Options
+            var grid =(Activator.CreateInstance(gridtype, new object[] { list }) as IDynamicGrid)!;
+            grid.Options
                 .BeginUpdate()
                 .Add(DynamicGridOption.AddRows)
                 .Add(DynamicGridOption.DeleteRows)
                 .Add(DynamicGridOption.DirectEdit)
                 .Add(DynamicGridOption.RecordCount)
                 .EndUpdate();
-            ((IDynamicGrid)grid).Refresh(true, true);
+            grid.Refresh(true, true);
+
+            (EditorDefinition as EmbeddedListEditor).CreateButtons(grid);
+            
             var window = new DynamicContentDialog((FrameworkElement)grid);
             if (window.ShowDialog() == true)
             {

+ 2 - 2
InABox.DynamicGrid/FormDesigner/DynamicFormLayoutGrid.cs

@@ -371,7 +371,7 @@ namespace InABox.DynamicGrid
             {
                 SaveItem(item);
                 Refresh(false, true);
-                OnChanged?.Invoke(this);
+                DoChanged();
             }
         }
 
@@ -386,7 +386,7 @@ namespace InABox.DynamicGrid
             {
                 SaveItem(item);
                 Refresh(false, true);
-                OnChanged?.Invoke(this);
+                DoChanged();
             }
         }
 

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

@@ -139,7 +139,7 @@ namespace InABox.DynamicGrid
                     {
                         DeleteItems(rows);
                         SelectedRows = Array.Empty<CoreRow>();
-                        OnChanged?.Invoke(this);
+                        DoChanged();
                         Refresh(false, true);
                         SelectItems(null);
                     }

+ 12 - 0
InABox.DynamicGrid/IDynamicGrid.cs

@@ -4,10 +4,20 @@
 using System;
 using System.Linq.Expressions;
 using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media.Imaging;
 using InABox.Core;
 
 namespace InABox.DynamicGrid
 {
+
+    public enum DynamicGridButtonPosition
+    {
+        Left,
+        Right
+    }
+    
+    
     public interface IDynamicGrid
     {
         FluentList<DynamicGridOption> Options { get; }
@@ -37,6 +47,8 @@ namespace InABox.DynamicGrid
 
         void AddVisualFilter(string column, string value);
 
+        Button AddButton(string caption, BitmapImage? image, string? tooltip, Func<Button, CoreRow[], bool> action, DynamicGridButtonPosition position = DynamicGridButtonPosition.Left);
+
         event OnDefineFilter OnDefineFilter;
 
         event OnPrintData OnPrintData;

+ 30 - 0
inabox.wpf/ImageUtils.cs

@@ -336,6 +336,29 @@ namespace InABox.WPF
 
             image.Freeze();
         }
+        
+        public static Bitmap? MergeBitmaps(IEnumerable<Bitmap> bitmaps, int padding)
+        {
+            if (!bitmaps.Any())
+                return null;
+            
+            var totalwidth = bitmaps.Aggregate(0, (total, next) => total + next.Width + (total > 0 ? padding : 0) );
+            var maxheight = bitmaps.Aggregate(0, (max, next) => Math.Max(next.Height,max) );
+
+            Bitmap result = new Bitmap(totalwidth, maxheight);  
+            using (Graphics g = Graphics.FromImage(result))  
+            {  
+                g.Clear(Color.Transparent);
+                int left = 0;
+                foreach (var bitmap in bitmaps)
+                {
+                    g.DrawImage(bitmap, left, 0);
+                    left += bitmap.Width + padding;
+                }
+            }
+            return result;  
+        }  
+        
 
         public static byte[] ToArray<T>(this BitmapImage image) where T : BitmapEncoder, new()
         {
@@ -368,6 +391,13 @@ namespace InABox.WPF
             return result;
         }
 
+        public static Bitmap Resize(this Bitmap bitmap, int width, int height)
+        {
+            if ((width == bitmap.Width) && (height == bitmap.Height))
+                return bitmap;
+            return new Bitmap(bitmap,new Size(width,height));
+        }
+
         public static BitmapImage Scale(this BitmapImage image, int maxheight, int maxwidth)
         {
             var scaleHeight = maxheight / (float)image.Height;