Browse Source

Implement DynamicTemplateColumn for custom/complex columns in data grids
DynamicDocumentGrid is now a single column templated display
Added Notes field to IEntityDocument
DataGrids now always show vertical scroll bar
Updated Syncfusion License

frogsoftware 1 year ago
parent
commit
e563dff11e

+ 3 - 0
InABox.Core/Classes/Document/EntityDocument.cs

@@ -28,6 +28,9 @@ namespace InABox.Core
 
         [NullEditor]
         public byte[]? Thumbnail { get; set; }
+        
+        [MemoEditor]
+        public String Notes { get; set; }
 
         //[NullEditor]
         //public string Annotations { get; set; }

+ 2 - 0
InABox.Core/Classes/Document/IEntityDocument.cs

@@ -10,5 +10,7 @@ namespace InABox.Core
         DateTime Superceded { get; set; }
         
         byte[]? Thumbnail { get; set; }
+        
+        String Notes { get; set; }
     }
 }

+ 1 - 1
InABox.Core/CoreUtils.cs

@@ -177,7 +177,7 @@ namespace InABox.Core
                 },
                 {
                     SyncfusionVersion.v23_2,
-                    "Mjg3NjYxM0AzMjMzMmUzMDJlMzBJNUxpZ2VaNTM2cXUrWnJWeEQ1Y09ONWc4eDVQdzZnaTUvSEZUaTBUdnhzPQ==;Mjg3NjYxNEAzMjMzMmUzMDJlMzBQcndYWWozblVvemZORm41VlhoWXBRbUt5d3BMUkJnU1M0OVdTZm5WLzRBPQ==;Mgo+DSMBaFt8QHJqUU1hXk5Hd0BLVGpAblJ3T2ZQdVt5ZDU7a15RRnVeQ19gSX5SdUZqW3xWcg==;Mgo+DSMBPh8sVXJ3S0R+WFpFdEBBXHxAd1p/VWJYdVt5flBPcDwsT3RfQF5iSHxQdkViWnxXcnFWTg==;ORg4AjUWIQA/Gnt2VlhiQlVPd11dXmJWd1p/THNYflR1fV9DaUwxOX1dQl9gSH9RdUVjWHxdeXdVQmI=;NRAiBiAaIQQuGjN/V0V+Xk9AfV5AQmBIYVp/TGpJfl96cVxMZVVBJAtUQF1hSn9SdEZjWX5dc31WR2lU;Mjg3NjYxOUAzMjMzMmUzMDJlMzBqN1hDUjdqV0xpcHJENjQwak5oMHVhamxHQnNjY0dYK1FSeVdPTUZkaW9RPQ==;Mjg3NjYyMEAzMjMzMmUzMDJlMzBGOGV5UEJ4dG9oRHowTklhR1JQeS81MU5VTm14T1lNMGNCVjVIVFkwNG5rPQ==;Mgo+DSMBMAY9C3t2VlhiQlVPd11dXmJWd1p/THNYflR1fV9DaUwxOX1dQl9gSH9RdUVjWHxdeXdTQGc=;Mjg3NjYyMkAzMjMzMmUzMDJlMzBNMFpuS0ZmRTE5TWZmWFZCZDYxU0M5bzcza0tPUDV6MjJBbG8vdXppWkE0PQ==;Mjg3NjYyM0AzMjMzMmUzMDJlMzBhUWZJbkZWSkJXZlI3N2ZhWXk0TFB4RU4xbVhyeXRzem13ZFJ2Vy9JZ0JvPQ==;Mjg3NjYyNEAzMjMzMmUzMDJlMzBqN1hDUjdqV0xpcHJENjQwak5oMHVhamxHQnNjY0dYK1FSeVdPTUZkaW9RPQ=="
+                    "Ngo9BigBOggjHTQxAR8/V1NHaF1cWmhIfEx1RHxQdld5ZFRHallYTnNWUj0eQnxTdEZiW35fcHRQRGVZWEBzVg=="
                 },
                 {
                     SyncfusionVersion.Unspecified,

+ 9 - 0
inabox.wpf/Converters/BytesToBitmapImageConverter.cs

@@ -0,0 +1,9 @@
+using System.Windows.Media.Imaging;
+
+namespace InABox.WPF;
+
+public class BytesToBitmapImageConverter : UtilityConverter<byte[], BitmapImage>
+{
+    public override BitmapImage Convert(byte[] value)
+        => ImageUtils.LoadImage(value);
+}

+ 0 - 45
inabox.wpf/DynamicGrid/Columns/DynamicActionColumn.cs

@@ -68,49 +68,4 @@ namespace InABox.DynamicGrid
         public abstract object? Data(CoreRow? row);
 
     }
-
-    public class DynamicTextColumn : DynamicActionColumn
-    {
-        public delegate string GetTextDelegate(CoreRow? row);
-
-        public GetTextDelegate Text { get; private set; }
-        
-        public Alignment Alignment { get; set; }
-        
-        public DynamicTextColumn(GetTextDelegate text, ActionDelegate? action = null)
-        {
-            Alignment = Alignment.MiddleCenter;
-            Text = text;
-            Action = action;
-            VerticalHeader = false;
-        }
-
-        public override object? Data(CoreRow? row) => Text?.Invoke(row);
-    }
-    
-    public class DynamicImageColumn : DynamicActionColumn
-    {
-        public delegate BitmapImage? GetImageDelegate(CoreRow? row);
-
-        public DynamicImageColumn(GetImageDelegate image, ActionDelegate? action = null)
-        {
-            Image = image;
-            Action = action;
-            VerticalHeader = true;
-        }
-
-        public DynamicImageColumn(BitmapImage image, ActionDelegate? action = null)
-        {
-            Image = r => image;
-            Action = action;
-        }
-
-        public GetImageDelegate Image { get; protected set; }
-
-
-        public bool AllowHeaderClick { get; set; } = false;
-        
-        public override object? Data(CoreRow? row) => Image?.Invoke(row);
-    }
-    
 }

+ 29 - 0
inabox.wpf/DynamicGrid/Columns/DynamicImageColumn.cs

@@ -0,0 +1,29 @@
+using System.Windows.Media.Imaging;
+using InABox.Core;
+
+namespace InABox.DynamicGrid;
+
+public class DynamicImageColumn : DynamicActionColumn
+{
+    public delegate BitmapImage? GetImageDelegate(CoreRow? row);
+
+    public DynamicImageColumn(GetImageDelegate image, ActionDelegate? action = null)
+    {
+        Image = image;
+        Action = action;
+        VerticalHeader = true;
+    }
+
+    public DynamicImageColumn(BitmapImage image, ActionDelegate? action = null)
+    {
+        Image = r => image;
+        Action = action;
+    }
+
+    public GetImageDelegate Image { get; protected set; }
+
+
+    public bool AllowHeaderClick { get; set; } = false;
+        
+    public override object? Data(CoreRow? row) => Image?.Invoke(row);
+}

+ 18 - 0
inabox.wpf/DynamicGrid/Columns/DynamicTemplateColumn.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Windows;
+using InABox.Core;
+
+namespace InABox.DynamicGrid;
+
+public class DynamicTemplateColumn : DynamicActionColumn
+{
+    public Func<FrameworkElement> Template { get; protected set; }
+    
+    public DynamicTemplateColumn(Func<FrameworkElement> template)
+    {
+        Template = template;
+        VerticalHeader = false;
+    }
+
+    public override object? Data(CoreRow? row) => row;
+}

+ 22 - 0
inabox.wpf/DynamicGrid/Columns/DynamicTextColumn.cs

@@ -0,0 +1,22 @@
+using InABox.Core;
+
+namespace InABox.DynamicGrid;
+
+public class DynamicTextColumn : DynamicActionColumn
+{
+    public delegate string GetTextDelegate(CoreRow? row);
+
+    public GetTextDelegate Text { get; private set; }
+        
+    public Alignment Alignment { get; set; }
+        
+    public DynamicTextColumn(GetTextDelegate text, ActionDelegate? action = null)
+    {
+        Alignment = Alignment.MiddleCenter;
+        Text = text;
+        Action = action;
+        VerticalHeader = false;
+    }
+
+    public override object? Data(CoreRow? row) => Text?.Invoke(row);
+}

+ 458 - 144
inabox.wpf/DynamicGrid/DynamicDocumentGrid.cs

@@ -3,21 +3,55 @@ using System.Collections.Generic;
 using System.Data;
 using System.Diagnostics;
 using System.Drawing;
+using System.Drawing.Printing;
 using System.IO;
 using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
 using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Media;
 using System.Windows.Media.Imaging;
+using FastReport;
 using InABox.Clients;
 using InABox.Core;
 using InABox.WPF;
 using Microsoft.Win32;
+using Microsoft.Xaml.Behaviors.Core;
+using RoslynPad.Editor;
 using Syncfusion.Pdf.Interactive;
 using Syncfusion.Pdf.Parsing;
 using Syncfusion.Pdf;
+using Syncfusion.Windows.PdfViewer;
+using Border = System.Windows.Controls.Border;
+using Color = System.Windows.Media.Color;
+using Image = System.Windows.Controls.Image;
 
 namespace InABox.DynamicGrid
 {
 
+    public class DocumentConverter : UtilityConverter<object, object>
+    {
+        public override object Convert(object value)
+        {
+            return value;
+        }
+    }
+    
+    public class TimeStampToBrushConverter : UtilityConverter<DateTime, System.Windows.Media.Brush?>
+    {
+        public System.Windows.Media.Brush? Empty { get; init; }
+        public System.Windows.Media.Brush? Set { get; init; }
+        
+        public override System.Windows.Media.Brush? Convert(DateTime value)
+        {
+            return value.IsEmpty()
+                ? Empty
+                : Set;
+        }
+    }
+    
     public delegate String OnGetWatermark(CoreRow row);
 
     public class DynamicDocumentGrid<TDocument, TEntity, TEntityLink> : DynamicManyToManyGrid<TDocument, TEntity>
@@ -25,19 +59,23 @@ namespace InABox.DynamicGrid
         where TDocument : Entity, IEntityDocument<TEntityLink>, IPersistent, IRemotable, new() // Entity, IPersistent, IRemotable, IManyToMany<TEntity, Document>, new()
         where TEntityLink : EntityLink<TEntity>, new()
     {
-        private DynamicActionColumn supercedecolumn;
+        // private DynamicActionColumn supercedecolumn;
+        //
+        // public bool ShowSupercededColumn
+        // {
+        //     get
+        //     {
+        //         return supercedecolumn.Position != DynamicActionColumnPosition.Hidden;
+        //     }
+        //     set
+        //     {
+        //         supercedecolumn.Position = value ? DynamicActionColumnPosition.End : DynamicActionColumnPosition.Hidden;
+        //     }
+        // }
+        
+        public bool ShowSupercededColumn { get; set; }
 
-        public bool ShowSupercededColumn
-        {
-            get
-            {
-                return supercedecolumn.Position != DynamicActionColumnPosition.Hidden;
-            }
-            set
-            {
-                supercedecolumn.Position = value ? DynamicActionColumnPosition.End : DynamicActionColumnPosition.Hidden;
-            }
-        }
+        private DynamicTemplateColumn _template;
 
         public DynamicDocumentGrid()
         {
@@ -45,178 +83,453 @@ namespace InABox.DynamicGrid
             HiddenColumns.Add(x => x.DocumentLink.ID);
             HiddenColumns.Add(x => x.Superceded);
             HiddenColumns.Add(x => x.DocumentLink.FileName);
-            ActionColumns.Add(new DynamicImageColumn(DocumentImage, ViewDocument) { Position = DynamicActionColumnPosition.Start });
-            ActionColumns.Add(new DynamicImageColumn(DiskImage, SaveDocument) { Position = DynamicActionColumnPosition.Start });
-            supercedecolumn = new DynamicImageColumn(SupercededImage, SupercedeDocument);
-            ActionColumns.Add(supercedecolumn);
+            HiddenColumns.Add(x => x.Thumbnail);
+            HiddenColumns.Add(x => x.Notes);
+            //ActionColumns.Add(new DynamicImageColumn(DocumentImage, ViewDocument) { Position = DynamicActionColumnPosition.Start });
+            //ActionColumns.Add(new DynamicImageColumn(DiskImage, SaveDocument) { Position = DynamicActionColumnPosition.Start });
+            _template = new DynamicTemplateColumn(DocumentTemplate)
+            {
+                Position = DynamicActionColumnPosition.Start,
+                Width = 0,
+                HeaderText = "Attached Documents"
+            };
+            ActionColumns.Add(_template);
+            //supercedecolumn = new DynamicImageColumn(SupercededImage, SupercedeDocument);
+            //ActionColumns.Add(supercedecolumn);
         }
 
-        protected override void DoReconfigure(FluentList<DynamicGridOption> options)
+        protected override void DoDoubleClick(object sender)
         {
-            base.DoReconfigure(options);
-
-            options.Add(DynamicGridOption.DragTarget);
+            var doc = SelectedRows.FirstOrDefault()?.ToObject<TDocument>();
+            if (doc != null)
+            {
+                var editor = new DocumentEditor(new IEntityDocument[] { doc });
+                //editor.PrintAllowed = Security.IsAllowed<CanPrintFactoryFloorDrawings>();
+                editor.SaveAllowed = false;
+                editor.ShowDialog();
+            }
         }
 
-        private bool SaveDocument(CoreRow? row)
+        private FrameworkElement DocumentTemplate()
         {
-            var filename = row.Get<TDocument, string>(x => x.DocumentLink.FileName);
-            if (Path.GetExtension(filename).ToUpper().Equals(".PDF"))
+            Border border = new Border()
+            {
+                CornerRadius = new CornerRadius(5),
+                Background = new SolidColorBrush(Colors.WhiteSmoke),
+                BorderBrush = new SolidColorBrush(Colors.Gray),
+                BorderThickness = new Thickness(0.75),
+                Margin = new Thickness(2),
+                Height = 100,
+                ContextMenu = new ContextMenu()
+            };
+            border.ContextMenu.Items.Add(new MenuItem()
             {
-                var dlg = new SaveFileDialog();
-                dlg.Filter = "PDF Files (*.pdf)|*.pdf";
-                dlg.FileName = Path.ChangeExtension(filename, ".pdf");
-                if (dlg.ShowDialog() == true)
+                Header = "View Documents",
+                Command = new ActionCommand(ViewDocuments)
+            });
+            border.ContextMenu.Items.Add(new MenuItem()
+            {
+                Header = "Copy To Clipboard",
+                Command = new ActionCommand(CopyDocuments)
+            });            
+            border.ContextMenu.Items.Add(new MenuItem()
+            {
+                Header = "Save Documents",
+                Command = new ActionCommand(SaveDocuments)
+            });
+            
+            border.SetBinding(
+                Border.BackgroundProperty,
+                new Binding("Superceded")
                 {
-                    var imageid = row.Get<TDocument, Guid>(x => x.DocumentLink.ID);
-                    var data = new Client<Document>().Query(new Filter<Document>(x => x.ID).IsEqualTo(imageid)).Rows.FirstOrDefault().Get<Document, byte[]>(x => x.Data);
-                    var name = dlg.FileName;
-                    File.WriteAllBytes(name, data);
-
-                    var gsProcessInfo = new ProcessStartInfo();
-                    gsProcessInfo.Verb = "open";
-                    gsProcessInfo.WindowStyle = ProcessWindowStyle.Normal;
-                    gsProcessInfo.FileName = name;
-                    gsProcessInfo.UseShellExecute = true;
-
-                    Process.Start(gsProcessInfo);
+                    Converter = new TimeStampToBrushConverter()
+                    {
+                        Empty = new SolidColorBrush(Colors.LightYellow),
+                        Set = new SolidColorBrush(Colors.Silver)
+                    }
                 }
-
-            }
-            else if (Path.GetExtension(filename).ToUpper().Equals(".PNG") || Path.GetExtension(filename).ToUpper().Equals(".JPG") || Path.GetExtension(filename).ToUpper().Equals(".GIF"))
+            );
+            Grid grid = new Grid()
             {
-                var imageid = row.Get<TDocument, Guid>(x => x.DocumentLink.ID);
-                if (imageid == Guid.Empty)
-                    return false;
-
-                var dlg = new SaveFileDialog();
-                dlg.Filter = "Image Files (*.png)|*.png";
-                dlg.FileName = filename;
-                if (dlg.ShowDialog() == true)
+                RowDefinitions =
                 {
-                    var bmp = LoadBitmapFromDatabase(imageid);
-                    bmp?.Save(dlg.FileName);
+                    new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) },
+                    new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) },
+                },
+                ColumnDefinitions =
+                {
+                    new ColumnDefinition() { Width = new GridLength(100) },
+                    new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
+                    new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) },
                 }
-            }
+            };
+            border.Child = grid;
+            
+            Image thumbnail = new Image()
+            {
+                Stretch = Stretch.Uniform,
+                Margin = new Thickness(2,2,5,2),
+                
+            };
+            
+            var ttImage = new Image();
+            ttImage.SetBinding(Image.SourceProperty,
+                new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
+            thumbnail.ToolTip = new ToolTip()
+            {
+                Content = ttImage
+            };
+            
+            thumbnail.SetBinding(Image.SourceProperty, new Binding("Thumbnail") { Converter = new BytesToBitmapImageConverter() });
+            thumbnail.SetValue(Grid.RowProperty,0);
+            thumbnail.SetValue(Grid.RowSpanProperty,2);
+            thumbnail.SetValue(Grid.ColumnProperty,0);
+            grid.Children.Add(thumbnail);
+            
+            var filename = new Label()
+            {
+                FontWeight = FontWeights.Bold
+            };
+            filename.SetBinding(Label.ContentProperty, new Binding("DocumentLink_FileName"));
+            filename.SetValue(Grid.RowProperty,0);
+            filename.SetValue(Grid.ColumnProperty,1);
+            grid.Children.Add(filename);
 
-            return false;
-        }
+            var buttons = new StackPanel()
+            {
+                Orientation = Orientation.Horizontal
+            };
+            buttons.SetValue(Grid.RowProperty,0);
+            buttons.SetValue(Grid.ColumnProperty,2);
+            grid.Children.Add(buttons);
 
-        private Bitmap LoadBitmapFromDatabase(Guid imageid)
-        {
-            if (imageid == Guid.Empty)
-                return null;
-            Bitmap result = null;
-            var image = new Client<Document>().Query(
-                new Filter<Document>(x => x.ID).IsEqualTo(imageid),
-                new Columns<Document>(x => x.ID, x => x.Data)
-            ).Rows.FirstOrDefault();
-            if (image != null)
+            var view = new Button()
             {
-                var ms = new MemoryStream(image.Get<Document, byte[]>(x => x.Data));
-                result = new Bitmap(ms);
-            }
+                Content = new Image() { Source = Wpf.Resources.multi_image.AsBitmapImage() },
+                BorderBrush = new SolidColorBrush(Colors.Transparent),
+                Background = new SolidColorBrush(Colors.Transparent),
+                Height = 32,
+                Width = 32,
+                Command = new ActionCommand(ViewDocuments)
+            };
+            buttons.Children.Add(view);
+            
+                        
+            var copy = new Button()
+            {
+                Content = new Image() { Source = Wpf.Resources.copy.AsBitmapImage() },
+                BorderBrush = new SolidColorBrush(Colors.Transparent),
+                Background = new SolidColorBrush(Colors.Transparent),
+                Height = 32,
+                Width = 32,
+                Command = new ActionCommand(CopyDocuments)
+            };
+            buttons.Children.Add(copy);
+            
+            var save = new Button()
+            {
+                Content = new Image() { Source = Wpf.Resources.download.AsBitmapImage() },
+                BorderBrush = new SolidColorBrush(Colors.Transparent),
+                Background = new SolidColorBrush(Colors.Transparent),
+                Height = 32,
+                Width = 32,
+                Command = new ActionCommand(SaveDocuments)
+            };
+            buttons.Children.Add(save);
 
-            return result;
+            var print = new Button()
+            {
+                Content = new Image() { Source = Wpf.Resources.print.AsBitmapImage() },
+                BorderBrush = new SolidColorBrush(Colors.Transparent),
+                Background = new SolidColorBrush(Colors.Transparent),
+                Height = 32,
+                Width = 32,
+                Command = new ActionCommand(PrintDocuments)
+            };
+            buttons.Children.Add(print);
+            
+            var notes = new Label()
+            {
+            };
+            notes.SetBinding(Label.ContentProperty, new Binding("Notes"));
+            notes.SetValue(Grid.RowProperty,1);
+            notes.SetValue(Grid.ColumnProperty,1);
+            notes.SetValue(Grid.ColumnSpanProperty,2);
+            grid.Children.Add(notes);
+            
+            return border;
         }
 
-        private BitmapImage? DiskImage(CoreRow? arg)
+        private void GetDocuments(Action<Dictionary<string,byte[]>> action)
         {
-            return Wpf.Resources.disk.AsBitmapImage();
+            var ids = SelectedRows.Select(r => r.Get<IEntityDocument, Guid>(c => c.DocumentLink.ID)).ToArray();
+            var files = new Client<Document>().Query(
+                new Filter<Document>(x => x.ID).InList(ids),
+                new Columns<Document>(x => x.FileName).Add(x => x.Data)
+            ).ToDictionary<Document, String, byte[]>(x => x.FileName, x => x.Data);
+            action?.Invoke(files);
         }
 
-        public override int Order()
+        private String SanitiseFileName(string filename)
         {
-            return int.MaxValue;
+            var basefilename = Path.GetFileNameWithoutExtension(filename);
+            var extension = Path.GetExtension(filename);
+            return Path.ChangeExtension(string.Join("_", basefilename.Split(Path.GetInvalidFileNameChars())), extension);
         }
 
-        private BitmapImage SupercededImage(CoreRow? row)
+        private void ViewDocuments()
         {
-            if (row == null)
-                return Wpf.Resources.tick.AsBitmapImage();
-            if (row.Get<TDocument, DateTime>(x => x.Superceded) != DateTime.MinValue)
-                return Wpf.Resources.warning.AsBitmapImage();
-            return Wpf.Resources.tick.AsBitmapImage();
+            GetDocuments((files) =>
+            {
+                
+                foreach (var file in files)
+                {
+                    Task.Run(() =>
+                    {
+                        var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
+                        File.WriteAllBytes(tempfile, file.Value);
+                        var info = new System.Diagnostics.ProcessStartInfo(tempfile);
+                        info.UseShellExecute = true;
+                        info.Verb = "Open";
+                        Process.Start(info);
+                    });
+                }
+            });
         }
 
-        private bool SupercedeDocument(CoreRow? row)
+        private void CopyDocuments()
         {
-            if (!ReadOnly)
+            if (SelectedRows?.Any() != true)
+                return;
+            GetDocuments((files) =>
             {
-                var id = row.Get<TDocument, Guid>(x => x.ID);
-                var document = WorkingList.FirstOrDefault(x => x.ID.Equals(id));
-                if (document != null)
-                    document.Superceded = document.Superceded == DateTime.MinValue ? DateTime.Now : DateTime.MinValue;
-                return true;
-            }
-            else
-            {
-                return false;
-            }
+                System.Collections.Specialized.StringCollection FileCollection = new System.Collections.Specialized.StringCollection();
+                foreach(var file in files)
+                {
+                    var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
+                    File.WriteAllBytes(tempfile, file.Value);
+                    FileCollection.Add(tempfile);
+                }
+                Clipboard.SetFileDropList(FileCollection);
+            });
         }
 
-        private BitmapImage DocumentImage(CoreRow? arg)
+        private void SaveDocuments()
         {
-            return Wpf.Resources.view.AsBitmapImage();
+            if (SelectedRows?.Any() != true)
+                return;
+            
+            using(var fbd = new System.Windows.Forms.FolderBrowserDialog())
+            {
+                var result = fbd.ShowDialog();
+                if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
+                {
+                    var path = fbd.SelectedPath;
+                    GetDocuments(files =>
+                    {
+                        foreach (var file in files)
+                            File.WriteAllBytes(Path.Combine(path, SanitiseFileName(file.Key)), file.Value);
+                    });
+                }
+            }
+
         }
 
-        private bool ViewDocument(CoreRow? row)
+        private void PrintDocuments()
         {
-            var filename = row.Get<TDocument, string>(x => x.DocumentLink.FileName);
-            if (Path.GetExtension(filename).ToUpper().Equals(".PDF"))
-            {
-                var viewer = new DocumentEditor(row.ToObject<TDocument>());
-                viewer.Watermark = OnGetWaterMark?.Invoke(row);
-                //viewer.PrintAllowed = true;
-                viewer.SaveAllowed = true;
-                viewer.ShowDialog();
-            }
-            else
+            if (SelectedRows?.Any() != true)
+                return;
+            GetDocuments(files =>
             {
-                var id = row.Get<TDocument, Guid>(x => x.DocumentLink.ID);
-                var docrow = new Client<Document>().Query(new Filter<Document>(x => x.ID).IsEqualTo(id)).Rows.FirstOrDefault();
-                if (docrow != null)
+                Task.Run(() =>
                 {
-                    var tmpfile = Path.ChangeExtension(Path.GetTempFileName(), Path.GetExtension(filename));
-                    File.WriteAllBytes(tmpfile, docrow.Get<Document, byte[]>(x => x.Data));
-                    var gsProcessInfo = new ProcessStartInfo();
-                    gsProcessInfo.Verb = "open";
-                    gsProcessInfo.WindowStyle = ProcessWindowStyle.Normal;
-                    gsProcessInfo.FileName = tmpfile;
-                    gsProcessInfo.UseShellExecute = true;
+                    foreach (var file in files)
+                    {
+                        var tempfile = Path.Combine(System.IO.Path.GetTempPath(), SanitiseFileName(file.Key));
+                        File.WriteAllBytes(tempfile, file.Value);
+                        var info = new System.Diagnostics.ProcessStartInfo(tempfile);
+                        info.CreateNoWindow = true;
+                        info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
+                        info.UseShellExecute = true;
+                        info.Verb = "print";
+                        Process.Start(info);
+                    }
+                });
+            });
 
-                    Process.Start(gsProcessInfo);
-                }
-                else
-                {
-                    MessageBox.Show(string.Format("Unable to retrieve {0}!", filename));
-                }
-            }
+        }
 
-            //Document doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
-            //if (doc != null)
-            //{
-            //    if (System.IO.Path.GetExtension(doc.FileName).ToUpper().Equals(".PDF"))
-            //    {
-            //        PDFViewer viewer = new PDFViewer(doc);
-            //        viewer.ShowDialog();
-            //    }
-            //    else
-            //    {
-            //        String filename = System.IO.Path.ChangeExtension(System.IO.Path.GetTempFileName(), System.IO.Path.GetExtension(doc.FileName));
-            //        System.IO.File.WriteAllBytes(filename, doc.Data);
-            //        ProcessStartInfo gsProcessInfo = new ProcessStartInfo();
-            //        gsProcessInfo.Verb = "open";
-            //        gsProcessInfo.WindowStyle = ProcessWindowStyle.Normal;
-            //        gsProcessInfo.UseShellExecute = true;
-            //        gsProcessInfo.FileName = filename;
-            //        Process.Start(gsProcessInfo);
-            //    }
-            //}
-            //else
-            //    MessageBox.Show("Document does nto exist!");
-            return false;
+        protected override DynamicGridColumns LoadColumns()
+        {
+            return new DynamicGridColumns();
         }
+        
+        protected override void DoReconfigure(FluentList<DynamicGridOption> options)
+        {
+            base.DoReconfigure(options);
+            options.Remove(DynamicGridOption.SelectColumns);
+            options.Add(DynamicGridOption.DragTarget);
+        }
+        
+        // private bool SaveDocument(CoreRow? row)
+        // {
+        //     var filename = row.Get<TDocument, string>(x => x.DocumentLink.FileName);
+        //     if (Path.GetExtension(filename).ToUpper().Equals(".PDF"))
+        //     {
+        //         var dlg = new SaveFileDialog();
+        //         dlg.Filter = "PDF Files (*.pdf)|*.pdf";
+        //         dlg.FileName = Path.ChangeExtension(filename, ".pdf");
+        //         if (dlg.ShowDialog() == true)
+        //         {
+        //             var imageid = row.Get<TDocument, Guid>(x => x.DocumentLink.ID);
+        //             var data = new Client<Document>().Query(new Filter<Document>(x => x.ID).IsEqualTo(imageid)).Rows.FirstOrDefault().Get<Document, byte[]>(x => x.Data);
+        //             var name = dlg.FileName;
+        //             File.WriteAllBytes(name, data);
+        //
+        //             var gsProcessInfo = new ProcessStartInfo();
+        //             gsProcessInfo.Verb = "open";
+        //             gsProcessInfo.WindowStyle = ProcessWindowStyle.Normal;
+        //             gsProcessInfo.FileName = name;
+        //             gsProcessInfo.UseShellExecute = true;
+        //
+        //             Process.Start(gsProcessInfo);
+        //         }
+        //
+        //     }
+        //     else if (Path.GetExtension(filename).ToUpper().Equals(".PNG") || Path.GetExtension(filename).ToUpper().Equals(".JPG") || Path.GetExtension(filename).ToUpper().Equals(".GIF"))
+        //     {
+        //         var imageid = row.Get<TDocument, Guid>(x => x.DocumentLink.ID);
+        //         if (imageid == Guid.Empty)
+        //             return false;
+        //
+        //         var dlg = new SaveFileDialog();
+        //         dlg.Filter = "Image Files (*.png)|*.png";
+        //         dlg.FileName = filename;
+        //         if (dlg.ShowDialog() == true)
+        //         {
+        //             var bmp = LoadBitmapFromDatabase(imageid);
+        //             bmp?.Save(dlg.FileName);
+        //         }
+        //     }
+        //
+        //     return false;
+        // }
+        //
+        // private Bitmap LoadBitmapFromDatabase(Guid imageid)
+        // {
+        //     if (imageid == Guid.Empty)
+        //         return null;
+        //     Bitmap result = null;
+        //     var image = new Client<Document>().Query(
+        //         new Filter<Document>(x => x.ID).IsEqualTo(imageid),
+        //         new Columns<Document>(x => x.ID, x => x.Data)
+        //     ).Rows.FirstOrDefault();
+        //     if (image != null)
+        //     {
+        //         var ms = new MemoryStream(image.Get<Document, byte[]>(x => x.Data));
+        //         result = new Bitmap(ms);
+        //     }
+        //
+        //     return result;
+        // }
+
+        // private BitmapImage? DiskImage(CoreRow? arg)
+        // {
+        //     return Wpf.Resources.disk.AsBitmapImage();
+        // }
+
+        public override int Order()
+        {
+            return int.MaxValue;
+        }
+
+        // private BitmapImage SupercededImage(CoreRow? row)
+        // {
+        //     if (row == null)
+        //         return Wpf.Resources.tick.AsBitmapImage();
+        //     if (row.Get<TDocument, DateTime>(x => x.Superceded) != DateTime.MinValue)
+        //         return Wpf.Resources.warning.AsBitmapImage();
+        //     return Wpf.Resources.tick.AsBitmapImage();
+        // }
+        //
+        // private bool SupercedeDocument(CoreRow? row)
+        // {
+        //     if (!ReadOnly)
+        //     {
+        //         var id = row.Get<TDocument, Guid>(x => x.ID);
+        //         var document = WorkingList.FirstOrDefault(x => x.ID.Equals(id));
+        //         if (document != null)
+        //             document.Superceded = document.Superceded == DateTime.MinValue ? DateTime.Now : DateTime.MinValue;
+        //         return true;
+        //     }
+        //     else
+        //     {
+        //         return false;
+        //     }
+        // }
+        //
+        // private BitmapImage DocumentImage(CoreRow? arg)
+        // {
+        //     return Wpf.Resources.view.AsBitmapImage();
+        // }
+        //
+        // private bool ViewDocument(CoreRow? row)
+        // {
+        //     var filename = row.Get<TDocument, string>(x => x.DocumentLink.FileName);
+        //     if (Path.GetExtension(filename).ToUpper().Equals(".PDF"))
+        //     {
+        //         var viewer = new DocumentEditor(row.ToObject<TDocument>());
+        //         viewer.Watermark = OnGetWaterMark?.Invoke(row);
+        //         //viewer.PrintAllowed = true;
+        //         viewer.SaveAllowed = true;
+        //         viewer.ShowDialog();
+        //     }
+        //     else
+        //     {
+        //         var id = row.Get<TDocument, Guid>(x => x.DocumentLink.ID);
+        //         var docrow = new Client<Document>().Query(new Filter<Document>(x => x.ID).IsEqualTo(id)).Rows.FirstOrDefault();
+        //         if (docrow != null)
+        //         {
+        //             var tmpfile = Path.ChangeExtension(Path.GetTempFileName(), Path.GetExtension(filename));
+        //             File.WriteAllBytes(tmpfile, docrow.Get<Document, byte[]>(x => x.Data));
+        //             var gsProcessInfo = new ProcessStartInfo();
+        //             gsProcessInfo.Verb = "open";
+        //             gsProcessInfo.WindowStyle = ProcessWindowStyle.Normal;
+        //             gsProcessInfo.FileName = tmpfile;
+        //             gsProcessInfo.UseShellExecute = true;
+        //
+        //             Process.Start(gsProcessInfo);
+        //         }
+        //         else
+        //         {
+        //             MessageBox.Show(string.Format("Unable to retrieve {0}!", filename));
+        //         }
+        //     }
+        //
+        //     //Document doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
+        //     //if (doc != null)
+        //     //{
+        //     //    if (System.IO.Path.GetExtension(doc.FileName).ToUpper().Equals(".PDF"))
+        //     //    {
+        //     //        PDFViewer viewer = new PDFViewer(doc);
+        //     //        viewer.ShowDialog();
+        //     //    }
+        //     //    else
+        //     //    {
+        //     //        String filename = System.IO.Path.ChangeExtension(System.IO.Path.GetTempFileName(), System.IO.Path.GetExtension(doc.FileName));
+        //     //        System.IO.File.WriteAllBytes(filename, doc.Data);
+        //     //        ProcessStartInfo gsProcessInfo = new ProcessStartInfo();
+        //     //        gsProcessInfo.Verb = "open";
+        //     //        gsProcessInfo.WindowStyle = ProcessWindowStyle.Normal;
+        //     //        gsProcessInfo.UseShellExecute = true;
+        //     //        gsProcessInfo.FileName = filename;
+        //     //        Process.Start(gsProcessInfo);
+        //     //    }
+        //     //}
+        //     //else
+        //     //    MessageBox.Show("Document does nto exist!");
+        //     return false;
+        // }
 
         public event OnGetWatermark OnGetWaterMark;
 
@@ -292,6 +605,7 @@ namespace InABox.DynamicGrid
                             prop.Synchronise(doc);
                             SaveItem(newitem);
                         }
+                        DoChanged();
                     }
                 }
 

+ 67 - 10
inabox.wpf/DynamicGrid/DynamicGrid.cs

@@ -433,7 +433,9 @@ namespace InABox.DynamicGrid
             DataGrid.KeyUp += DataGrid_KeyUp;
             DataGrid.PreviewGotKeyboardFocus += DataGrid_PreviewGotKeyboardFocus;
             //DataGrid.SelectionController = new GridSelectionControllerExt(DataGrid);
-
+            
+            DataGrid.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Visible);
+            
             DataGrid.FilterChanged += DataGrid_FilterChanged;
 
             DataGrid.FilterItemsPopulating += DataGrid_FilterItemsPopulating;
@@ -1514,11 +1516,15 @@ namespace InABox.DynamicGrid
 
             Dispatcher.BeginInvoke(() =>
             {
-                var fAvailWidth = width;
-
+                //var vc = DataGrid.GetVisualContainer();
+                //vc.RowHeightManager.Reset();
+                //vc.InvalidateMeasureInfo();
+                
+                var fAvailWidth = width - (SystemParameters.VerticalScrollBarWidth);
+                
                 //if (Data.Rows.Count * (DataGrid.RowHeight + 1) + DataGrid.HeaderRowHeight > height + 0.5F)
-                if (height < DataGrid.AutoScroller.VScrollBar.Maximum)
-                    fAvailWidth -= (SystemParameters.VerticalScrollBarWidth + 0.75);
+                //if (height < DataGrid.AutoScroller.VScrollBar.Maximum)
+                //    fAvailWidth -= (SystemParameters.VerticalScrollBarWidth + 0.75);
 
 
                 double fCurWidth = 0.0F;
@@ -1529,11 +1535,27 @@ namespace InABox.DynamicGrid
                 for (var i = 0; i < ColumnList.Count; i++)
                 {
                     var col = ColumnList[i];
-                    if (col is DynamicActionColumn dac)
+                    if (col is DynamicImageColumn dic)
                     {
-                        colWidths[i] = dac.Width == 0 ? RowHeight : dac.Width;
+                        colWidths[i] = RowHeight;
                         fCurWidth += colWidths[i];
                     }
+                    else if (col is DynamicTextColumn dxc)
+                    {
+                        colWidths[i] = dxc.Width;
+                        if (dxc.Width != 0)
+                            fCurWidth += Math.Max(0.0F, dxc.Width);
+                        else
+                            NumAutoCols++;
+                    }
+                    else if (col is DynamicTemplateColumn dtc)
+                    {
+                        colWidths[i] = dtc.Width;
+                        if (dtc.Width != 0)
+                            fCurWidth += Math.Max(0.0F, dtc.Width);
+                        else
+                            NumAutoCols++;
+                    }
                     else if (col is DynamicGridColumn dgc)
                     {
                         colWidths[i] = dgc.Width;
@@ -1656,7 +1678,7 @@ namespace InABox.DynamicGrid
                         newcol.AllowEditing = false;
                         newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
                         newcol.MappingName = sColName;
-                        newcol.Width = column.Width == 0 ? DataGrid.RowHeight : column.Width;
+                        newcol.Width = column.Width;
                         newcol.ColumnSizer = GridLengthUnitType.None;
                         newcol.HeaderText = column.HeaderText;
                         newcol.AllowFiltering = column.Filters != null && column.Filters.Any();
@@ -1687,18 +1709,53 @@ namespace InABox.DynamicGrid
                         DataGrid.Columns.Add(newcol);
                         ColumnList.Add(column);
                     }
+                    else if (column is DynamicTemplateColumn tmplCol)
+                    {
+                        var newcol = new GridTemplateColumn();
+                        newcol.CellTemplateSelector = new TemplateColumnSelector() { DataTemplate = tmplCol.Template };
+                        newcol.AllowEditing = false;
+                        newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
+                        
+                        newcol.Width = tmplCol.Width;
+                        newcol.ColumnSizer = GridLengthUnitType.None;
+                        newcol.HeaderText = column.HeaderText;
+                        newcol.AllowFiltering = false;
+                        newcol.AllowSorting = false;
+                        newcol.FilterRowOptionsVisibility = Visibility.Collapsed;
+                        newcol.ShowToolTip = false;
+
+                        var style = new Style();
+                        style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
+                        style.Setters.Add(new Setter(IsEnabledProperty, false));
+                        newcol.FilterRowCellStyle = style;
 
+                        var headstyle = new Style(typeof(GridHeaderCellControl));
+                        headstyle.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
+                        headstyle.Setters.Add(new Setter(ForegroundProperty, new SolidColorBrush(Colors.Black)));
+                        headstyle.Setters.Add(new Setter(FontSizeProperty, 12D));
+                        headstyle.Setters.Add(new Setter(MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
+                        headstyle.Setters.Add(new Setter(BorderThicknessProperty, new Thickness(0.75)));
+                        newcol.HeaderStyle = headstyle;
 
+                        DataGrid.Columns.Add(newcol);
+                        ColumnList.Add(column);
+                    }
                 }
             }
         }
+        
+        private class TemplateColumnSelector : DataTemplateSelector
+        {
+            public Func<FrameworkElement> DataTemplate { get; init; }
+            
+            public override DataTemplate SelectTemplate(object item, DependencyObject container) 
+                => TemplateGenerator.CreateDataTemplate(DataTemplate);
+        }
 
         private bool CanSort()
         {
             return !IsSequenced;
         }
-
-        
         
         private void ReloadColumns()
         {

+ 11 - 9
inabox.wpf/InABox.Wpf.csproj

@@ -131,16 +131,18 @@
         <PackageReference Include="FastReport.Net.Pro" Version="2023.2.23" />
         <PackageReference Include="GhostScript.NetCore" Version="1.0.1" />
         <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.0.1" />
-        <PackageReference Include="Syncfusion.Grid.WPF" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.Pdf.Wpf" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.PdfViewer.WPF" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.SfGrid.WPF" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.SfImageEditor.WPF" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.SfInput.WPF" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.SfRichTextBoxAdv.WPF" Version="23.2.6" />
-        <PackageReference Include="Syncfusion.SfSpreadsheet.WPF" Version="23.2.6" />
+        <PackageReference Include="net.sf.mpxj-ikvm" Version="10.9.0" />
+        <PackageReference Include="Syncfusion.Grid.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.Pdf.Wpf" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.PdfToImageConverter.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.PdfViewer.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.SfGrid.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.SfImageEditor.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.SfInput.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.SfRichTextBoxAdv.WPF" Version="23.2.7" />
+        <PackageReference Include="Syncfusion.SfSpreadsheet.WPF" Version="23.2.7" />
         <PackageReference Include="Syncfusion.Tools.WPF.Classic" Version="19.4.0.56" />
-        <PackageReference Include="Syncfusion.XlsIO.Wpf" Version="23.2.6" />
+        <PackageReference Include="Syncfusion.XlsIO.Wpf" Version="23.2.7" />
         <PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
     </ItemGroup>