Sfoglia il codice sorgente

Added ability to make a DynamicGrid non-modally edit.

Kenric Nugteren 7 mesi fa
parent
commit
d4f44897cd

+ 4 - 0
inabox.wpf/DynamicGrid/DynamicEditorForm/DynamicEditorForm.xaml.cs

@@ -246,6 +246,10 @@ public partial class DynamicEditorForm : ThemableChromelessWindow, IDynamicEdito
                     return;
             }
         }
+        else
+        {
+            Close();
+        }
     }
 
     #endregion

+ 4 - 0
inabox.wpf/DynamicGrid/DynamicEditorForm/EmbeddedDynamicEditorForm.xaml.cs

@@ -53,6 +53,10 @@ namespace InABox.DynamicGrid
                 _items = value;
                 DynamicEditorFormModel.Slug = Items?.FirstOrDefault()?.GetType().EntityName().Split('.').Last() ?? "";
                 Editor.Load(Pages);
+
+                bChanged = _items.Any(x => x is not Entity e || e.ID == Guid.Empty);
+                UpdateButtonEnabled();
+
                 foreach (var page in Pages)
                     page.OnChanged += (sender, args) => DoChanged();
             }

+ 122 - 20
inabox.wpf/DynamicGrid/DynamicGrid.cs

@@ -1662,7 +1662,64 @@ public abstract class DynamicGrid<T> : DynamicGrid, IDynamicGridUIComponentParen
         OnBeforeSave?.Invoke(editor, items);
     }
 
+    /// <summary>
+    /// Edit the <paramref name="items"/> with a non-modal editor window, attaching them to the <paramref name="host"/> provided.
+    /// </summary>
+    /// <param name="items">List of objects to edit.</param>
+    /// <param name="callback">A callback for when the items are finished being edited.</param>
+    public virtual void EditItemsNonModal(ISubPanelHost host, T[] items, Action<T[], bool> callback, Func<Type, CoreTable?>? PageDataHandler = null, bool PreloadPages = false)
+    {
+        if (!DynamicGridUtils.TryEdit(items, out var editLock))
+        {
+            if(editLock.Panel is Window window)
+            {
+                Task.Delay(100).ContinueWith(task =>
+                {
+                    window.WindowState = WindowState.Normal;
+                    window.Activate();
+                }, TaskScheduler.FromCurrentSynchronizationContext());
+            }
+            else
+            {
+                MessageWindow.ShowMessage("One or more items are already being edited in an open window. You cannot edit the same entity multiple times simultaneously.", "Simultaneous edit.");
+            }
+            return;
+        }
 
+        DynamicEditorForm editor;
+        using (var cursor = new WaitCursor())
+        {
+            editor = new DynamicEditorForm();
+            editor.SetValue(Panel.ZIndexProperty, 999);
+            editor.Form.DisableOKIfUnchanged = true;
+
+            InitialiseEditorForm(editor, items, PageDataHandler, PreloadPages);
+            OnEditorLoaded?.Invoke(editor, items);
+        }
+        editLock.Panel = editor;
+
+        host.AddSubPanel(editor);
+        editor.SubPanelClosed += o =>
+        {
+            try
+            {
+                DynamicGridUtils.FinishEdit(items);
+                callback(items, editor.Result == true);
+            }
+            catch(Exception e)
+            {
+                MessageWindow.ShowError("Error occurred while closing editor.", e);
+            }
+        };
+
+        editor.Show();
+    }
+
+    /// <summary>
+    /// Edit the <paramref name="items"/> with a modal editor window.
+    /// </summary>
+    /// <param name="items">List of objects to edit.</param>
+    /// <returns><see langword="true"/> if the items were saved.</returns>
     public virtual bool EditItems(T[] items, Func<Type, CoreTable?>? PageDataHandler = null, bool PreloadPages = false)
     {
         DynamicEditorForm editor;
@@ -1844,19 +1901,38 @@ public abstract class DynamicGrid<T> : DynamicGrid, IDynamicGridUIComponentParen
             //foreach (String key in VisualFilters.Keys)
             //    CoreUtils.SetPropertyValue(item, key, VisualFilters[key]);
 
-            if (EditItems([item]))
+            if(Options.NonModalEditorHost is not null)
             {
-                //_CurrentRow = Data.Rows.Count;
-                var row = Data.NewRow();
-                ObjectToRow(item, row);
-                Data.Rows.Add(row);
-                InvalidateGrid();
-                SelectedRows = [row];
-                DoChanged();
-                return true;
+                EditItemsNonModal(Options.NonModalEditorHost, [item], (items, result) =>
+                {
+                    if (result)
+                    {
+                        var row = Data.NewRow();
+                        ObjectToRow(item, row);
+                        Data.Rows.Add(row);
+                        InvalidateGrid();
+                        SelectedRows = [row];
+                        DoChanged();
+                    }
+                });
+                return false;
             }
+            else
+            {
+                if (EditItems([item]))
+                {
+                    //_CurrentRow = Data.Rows.Count;
+                    var row = Data.NewRow();
+                    ObjectToRow(item, row);
+                    Data.Rows.Add(row);
+                    InvalidateGrid();
+                    SelectedRows = [row];
+                    DoChanged();
+                    return true;
+                }
 
-            return false;
+                return false;
+            }
         }
 
         var items = Array.Empty<T>();
@@ -1872,22 +1948,48 @@ public abstract class DynamicGrid<T> : DynamicGrid, IDynamicGridUIComponentParen
         if (items.Length != 0)
         {
             var snaps = items.ToArray(x => x.TakeSnapshot());
-            if (EditItems(items))
+
+            if(Options.NonModalEditorHost is not null)
             {
-                var sel = SelectedRows;
-                UpdateRows(rows, items, invalidateRows: false);
-                InvalidateGrid();
-                SelectedRows = sel;
-                DoChanged();
-                return true;
+                EditItemsNonModal(Options.NonModalEditorHost, items, (items, result) =>
+                {
+                    if (result)
+                    {
+                        var sel = SelectedRows;
+                        UpdateRows(rows, items, invalidateRows: false);
+                        InvalidateGrid();
+                        SelectedRows = sel;
+                        DoChanged();
+                    }
+                    else
+                    {
+                        foreach(var snap in snaps)
+                        {
+                            snap.ResetObject();
+                        }
+                    }
+                });
+                return false;
             }
             else
             {
-                foreach(var snap in snaps)
+                if (EditItems(items))
                 {
-                    snap.ResetObject();
+                    var sel = SelectedRows;
+                    UpdateRows(rows, items, invalidateRows: false);
+                    InvalidateGrid();
+                    SelectedRows = sel;
+                    DoChanged();
+                    return true;
                 }
-            } 
+                else
+                {
+                    foreach(var snap in snaps)
+                    {
+                        snap.ResetObject();
+                    }
+                } 
+            }
             return false;
         }
 

+ 16 - 0
inabox.wpf/DynamicGrid/DynamicGridCommon.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using System.Windows.Controls;
 using InABox.Core;
+using InABox.Wpf;
 
 namespace InABox.DynamicGrid;
 
@@ -77,6 +78,7 @@ public class DynamicGridOptions
         HideDatabaseFilters = false;
         HideDirectEditButton = false;
 		PageSize = 0;
+		NonModalEditorHost = null;
         return EndUpdate();
     }
 
@@ -319,6 +321,20 @@ public class DynamicGridOptions
 		}
 	}
 
+	private ISubPanelHost? _nonModalEditorHost;
+	public ISubPanelHost? NonModalEditorHost
+	{
+		get => _nonModalEditorHost;
+		set
+		{
+			if(_nonModalEditorHost != value)
+			{
+				_nonModalEditorHost = value;
+				Changed();
+			}
+		}
+	}
+
 	private int _pageSize = 0;
 	/// <summary>
 	/// The page size for loading data in pages; set to 0 for no paging functionality.

+ 65 - 0
inabox.wpf/DynamicGrid/DynamicGridUtils.cs

@@ -493,6 +493,71 @@ public static class DynamicGridUtils
 
     #endregion
 
+    #region Non-modal Editing
+
+    public class DynamicGridEntityEditLock(IEnumerable<Guid> ids)
+    {
+        public HashSet<Guid> ObjectIDs { get; set; } = ids.ToHashSet();
+
+        public ISubPanel? Panel { get; set; }
+    }
+
+    private static Dictionary<Guid, DynamicGridEntityEditLock> CurrentEditLocks = new();
+
+    /// <summary>
+    /// Attempt to begin editing <paramref name="objs"/>, failing if those entities are already being edited elsewhere.
+    /// </summary>
+    /// <remarks>
+    /// If returns <see langword="true"/>, then <paramref name="editLock"/> will contain a new edit lock which contains the
+    /// new entities. Otherwise, <paramref name="editLock"/> will be the lock on the entities that are already being edited.
+    /// </remarks>
+    public static bool TryEdit(BaseObject[] objs, out DynamicGridEntityEditLock editLock)
+    {
+        lock (CurrentEditLocks)
+        {
+            var ids = new List<Guid>();
+            foreach(var obj in objs)
+            {
+                if(obj is Entity entity && entity.ID != Guid.Empty)
+                {
+                    if (CurrentEditLocks.TryGetValue(entity.ID, out editLock))
+                    {
+                        return false;
+                    }
+                    else
+                    {
+                        ids.Add(entity.ID);
+                    }
+                }
+            }
+            editLock = new(ids);
+            foreach(var id in ids)
+            {
+                CurrentEditLocks.Add(id, editLock);
+            }
+            return true;
+        }
+    }
+
+    public static void FinishEdit(BaseObject[] objs)
+    {
+        lock (CurrentEditLocks)
+        {
+            foreach(var obj in objs)
+            {
+                if(obj is Entity entity && entity.ID != Guid.Empty)
+                {
+                    if(CurrentEditLocks.Remove(entity.ID, out var editLock))
+                    {
+                        editLock.ObjectIDs.Remove(entity.ID);
+                    }
+                }
+            }
+        }
+    }
+
+    #endregion
+
     #region Editing BaseObject
 
     /// <summary>