Преглед изворни кода

- Added SubPanel system, for keeping track of non-modal windows.
- Made DynamiceditorForm a SubPanel, and changed the ShowDialog() method.

Kenric Nugteren пре 9 месеци
родитељ
комит
5dd3c1a9ba
2 измењених фајлова са 259 додато и 141 уклоњено
  1. 190 141
      inabox.wpf/DynamicGrid/DynamicEditorForm/DynamicEditorForm.xaml.cs
  2. 69 0
      inabox.wpf/Panel/IPanel.cs

+ 190 - 141
inabox.wpf/DynamicGrid/DynamicEditorForm/DynamicEditorForm.xaml.cs

@@ -9,195 +9,244 @@ using InABox.WPF;
 using Syncfusion.Windows.Shared;
 using Syncfusion.Windows.Tools.Controls;
 
+namespace InABox.DynamicGrid;
 
-namespace InABox.DynamicGrid
+public class DynamicEditorFormModel
 {
-    
-    public class DynamicEditorFormModel
+    /// <summary>
+    ///     Constructor of the UtilityViewModel class.
+    /// </summary>
+    public DynamicEditorFormModel()
     {
-        /// <summary>
-        ///     Constructor of the UtilityViewModel class.
-        /// </summary>
-        public DynamicEditorFormModel()
-        {
-            var utilities = new ObservableCollection<UtilityItem>();
-            utilities.Add(new UtilityItem
-                { Name = "Help", Icon = Resources.help.AsBitmapImage(), Text = "", Mode = SizeMode.Normal, Command = HelpCommand });
-            Utilities = utilities;
-        }
-
-        /// <summary>
-        ///     Collection containing the complete details of the items to be bound in the title bar.
-        /// </summary>
-        public ObservableCollection<UtilityItem> Utilities { get; }
+        var utilities = new ObservableCollection<UtilityItem>();
+        utilities.Add(new UtilityItem
+            { Name = "Help", Icon = Resources.help.AsBitmapImage(), Text = "", Mode = SizeMode.Normal, Command = HelpCommand });
+        Utilities = utilities;
+    }
 
-        /// <summary>
-        ///     Commmand for the Help button.
-        /// </summary>
-        public DelegateCommand HelpCommand => new(HelpCommandAction);
+    /// <summary>
+    ///     Collection containing the complete details of the items to be bound in the title bar.
+    /// </summary>
+    public ObservableCollection<UtilityItem> Utilities { get; }
 
-        public static string Slug { get; set; }
+    /// <summary>
+    ///     Commmand for the Help button.
+    /// </summary>
+    public DelegateCommand HelpCommand => new(HelpCommandAction);
 
-        /// <summary>
-        ///     Action that is performed when clicking the help button.
-        /// </summary>
-        private void HelpCommandAction(object param)
-        {
-            Process.Start("https://prsdigital.com.au/wiki/index.php/" + Slug);
-        }
-    }
+    public static string Slug { get; set; }
 
     /// <summary>
-    ///     Interaction logic for DynamicEditor.xaml
+    ///     Action that is performed when clicking the help button.
     /// </summary>
-    public partial class DynamicEditorForm : ThemableChromelessWindow, IDynamicEditorForm
+    private void HelpCommandAction(object param)
     {
-        #region IDynamicEditorForm
+        Process.Start("https://prsdigital.com.au/wiki/index.php/" + Slug);
+    }
+}
 
-        public bool ReadOnly { get => Form.ReadOnly; set => Form.ReadOnly = value; }
-        public BaseObject[] Items { get => Form.Items; set => Form.Items = value; }
-        public DynamicEditorPages? Pages { get => Form.Pages; }
+/// <summary>
+///     Interaction logic for DynamicEditor.xaml
+/// </summary>
+public partial class DynamicEditorForm : ThemableChromelessWindow, IDynamicEditorForm, ISubPanel
+{
+    #region IDynamicEditorForm
 
-        public event OnBeforeLoad? OnBeforeLoad;
-        public void BeforeLoad() => OnBeforeLoad?.Invoke(this);
+    public bool ReadOnly { get => Form.ReadOnly; set => Form.ReadOnly = value; }
+    public BaseObject[] Items { get => Form.Items; set => Form.Items = value; }
+    public DynamicEditorPages? Pages { get => Form.Pages; }
 
-        public event OnAfterLoad? OnAfterLoad;
-        public void AfterLoad() => OnAfterLoad?.Invoke(this);
+    public event OnBeforeLoad? OnBeforeLoad;
+    public void BeforeLoad() => OnBeforeLoad?.Invoke(this);
 
-        public event OnValidateData? OnValidateData { add => Form.OnValidateData += value; remove => Form.OnValidateData -= value; }
+    public event OnAfterLoad? OnAfterLoad;
+    public void AfterLoad() => OnAfterLoad?.Invoke(this);
 
-        public OnCustomiseColumns? OnCustomiseColumns
-        {
-            get => Form.OnCustomiseColumns; 
-            set => Form.OnCustomiseColumns = value;
-        }
-        public OnDefineLookupFilter? OnDefineFilter { get => Form.OnDefineFilter; set { Form.OnDefineFilter = value; } }
+    public event OnValidateData? OnValidateData { add => Form.OnValidateData += value; remove => Form.OnValidateData -= value; }
 
-        public OnDefineLookup? OnDefineLookups { get => Form.OnDefineLookups; set { Form.OnDefineLookups = value; } }
+    public OnCustomiseColumns? OnCustomiseColumns
+    {
+        get => Form.OnCustomiseColumns; 
+        set => Form.OnCustomiseColumns = value;
+    }
+    public OnDefineLookupFilter? OnDefineFilter { get => Form.OnDefineFilter; set { Form.OnDefineFilter = value; } }
 
-        public DefineEditorEventHandler? OnDefineEditor { get => Form.OnDefineEditor; set { Form.OnDefineEditor = value; } }
-        public event OnFormCustomiseEditor? OnFormCustomiseEditor;
-        public OnReconfigureEditors? OnReconfigureEditors { get => Form.OnReconfigureEditors; set { Form.OnReconfigureEditors = value; } }
-        
-        public event OnAfterEditorValueChanged? OnAfterEditorValueChanged { add => Form.OnAfterEditorValueChanged += value; remove => Form.OnAfterEditorValueChanged -= value; }
-        public event EditorValueChangedHandler? OnEditorValueChanged { add => Form.OnEditorValueChanged += value; remove => Form.OnEditorValueChanged -= value; }
+    public OnDefineLookup? OnDefineLookups { get => Form.OnDefineLookups; set { Form.OnDefineLookups = value; } }
 
+    public DefineEditorEventHandler? OnDefineEditor { get => Form.OnDefineEditor; set { Form.OnDefineEditor = value; } }
+    public event OnFormCustomiseEditor? OnFormCustomiseEditor;
 
-        public event OnSelectPage? OnSelectPage { add => Form.OnSelectPage += value; remove => Form.OnSelectPage -= value; }
+    public OnReconfigureEditors? OnReconfigureEditors { get => Form.OnReconfigureEditors; set { Form.OnReconfigureEditors = value; } }
+    
+    public event OnAfterEditorValueChanged? OnAfterEditorValueChanged { add => Form.OnAfterEditorValueChanged += value; remove => Form.OnAfterEditorValueChanged -= value; }
+    public event EditorValueChangedHandler? OnEditorValueChanged { add => Form.OnEditorValueChanged += value; remove => Form.OnEditorValueChanged -= value; }
 
-        public DynamicGridSaveEvent? OnSaveItem { get => Form.OnSaveItem; set { Form.OnSaveItem = value; } }
 
+    public event OnSelectPage? OnSelectPage { add => Form.OnSelectPage += value; remove => Form.OnSelectPage -= value; }
 
-        public IDynamicEditorControl FindEditor(string columnName) => Form.FindEditor(columnName);
-        public object? GetEditorValue(string columnName) => Form.GetEditorValue(columnName);
-        public void SetEditorValue(string columnName, object? value) => Form.SetEditorValue(columnName, value);
+    public DynamicGridSaveEvent? OnSaveItem { get => Form.OnSaveItem; set { Form.OnSaveItem = value; } }
 
-        public void UnloadEditorPages(bool saved) => Form.UnloadEditorPages(saved);
 
-        #endregion
+    public IDynamicEditorControl FindEditor(string columnName) => Form.FindEditor(columnName);
+    public object? GetEditorValue(string columnName) => Form.GetEditorValue(columnName);
+    public void SetEditorValue(string columnName, object? value) => Form.SetEditorValue(columnName, value);
 
-        public DynamicEditorForm()
-        {
-            InitializeComponent();
-            
-            //this.Loaded += new RoutedEventHandler(ConfigureSystemMenu);
-
-            Form.OnEditorCreated += Editor_OnEditorCreated;
-            Form.OnOK += Form_OnOK;
-            Form.OnCancel += Form_OnCancel;
-            Form.OnFormCustomiseEditor += (sender, items, column, editor) => OnFormCustomiseEditor?.Invoke(sender, items, column, editor);
-        }
+    public void UnloadEditorPages(bool saved) => Form.UnloadEditorPages(saved);
 
-        public DynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
-            Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false): this()
-        {
-            Setup(type, pages, buttons, pageDataHandler, preloadPages);
-        }
+    #endregion
 
-        public void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
-            Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false)
-        {
-            Form.Setup(type, pages, buttons, pageDataHandler, preloadPages);
-        }
-        public void SetLayoutType<T>() where T : DynamicEditorGridLayout => Form.SetLayoutType<T>();
+    public bool? Result { get; set; }
+
+    public DynamicEditorForm()
+    {
+        InitializeComponent();
+        
+        //this.Loaded += new RoutedEventHandler(ConfigureSystemMenu);
 
-        private void Form_OnCancel()
+        Form.OnEditorCreated += Editor_OnEditorCreated;
+        Form.OnOK += Form_OnOK;
+        Form.OnCancel += Form_OnCancel;
+        Form.OnFormCustomiseEditor += (sender, items, column, editor) => OnFormCustomiseEditor?.Invoke(sender, items, column, editor);
+    }
+
+    public DynamicEditorForm(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+        Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false): this()
+    {
+        Setup(type, pages, buttons, pageDataHandler, preloadPages);
+    }
+
+    public void Setup(Type type, DynamicEditorPages? pages = null, DynamicEditorButtons? buttons = null,
+        Func<Type, CoreTable?>? pageDataHandler = null, bool preloadPages = false)
+    {
+        Form.Setup(type, pages, buttons, pageDataHandler, preloadPages);
+    }
+    public void SetLayoutType<T>() where T : DynamicEditorGridLayout => Form.SetLayoutType<T>();
+
+    // Providing new implementation to avoid using DIalogResult, which breaks if non-modal.
+    public new bool? ShowDialog()
+    {
+        base.ShowDialog();
+        return Result;
+    }
+
+    private void Form_OnCancel()
+    {
+        if (bChanged)
         {
-            if (bChanged)
+            var result = MessageWindow.ShowYesNoCancel("Save Changes?", "Confirm");
+            switch (result)
             {
-                var result = MessageWindow.ShowYesNoCancel("Save Changes?", "Confirm");
-                switch (result)
-                {
-                    case MessageWindowResult.Yes:
-                        DialogResult = true;
-                        break;
-                    case MessageWindowResult.No:
-                        DialogResult = false;
-                        break;
-                }
+                case MessageWindowResult.Yes:
+                    Result = true;
+                    Close();
+                    break;
+                case MessageWindowResult.No:
+                    Result = false;
+                    Close();
+                    break;
             }
-            else
-                DialogResult = false;
         }
-
-        private void Form_OnOK()
+        else
         {
-            DialogResult = true;
+            Result = false;
+            Close();
         }
+    }
 
-        private void Editor_OnEditorCreated(object sender, double height, double width)
-        {
-        }
+    private void Form_OnOK()
+    {
+        Result = true;
+        Close();
+    }
+
+    private void Editor_OnEditorCreated(object sender, double height, double width)
+    {
+    }
 
-        private void Window_Closing(object sender, CancelEventArgs e)
+    private void Window_Closing(object sender, CancelEventArgs e)
+    {
+        if (bChanged && Result == null)
         {
-            if (bChanged && DialogResult == null)
+            var result = MessageWindow.ShowYesNoCancel("Save Changes?", "Confirm");
+            switch (result)
             {
-                var result = MessageWindow.ShowYesNoCancel("Save Changes?", "Confirm");
-                switch (result)
-                {
-                    case MessageWindowResult.Yes:
-                        DialogResult = true;
-                        break;
-                    case MessageWindowResult.No:
-                        DialogResult = false;
-                        break;
-                    case MessageWindowResult.Cancel:
-                        e.Cancel = true;
-                        return;
-                }
+                case MessageWindowResult.Yes:
+                    Result = true;
+                    break;
+                case MessageWindowResult.No:
+                    Result = false;
+                    break;
+                case MessageWindowResult.Cancel:
+                    e.Cancel = true;
+                    return;
             }
-            if (DialogResult == true)
-                Form.SaveItem(e);
         }
+        if (Result == true)
+            Form.SaveItem(e);
+        SubPanelClosed?.Invoke(this);
+    }
 
-        private bool bChanged = false;
-        private void Form_OnOnChanged(object? sender, EventArgs e)
-        {
-            bChanged = true;
-        }
+    private bool bChanged = false;
+    private void Form_OnOnChanged(object? sender, EventArgs e)
+    {
+        bChanged = true;
+    }
 
-        private void ThemableChromelessWindow_Loaded(object sender, RoutedEventArgs e)
-        {
-            var screen = WpfScreen.GetScreenFrom(new Point(Left, Top));
+    private void ThemableChromelessWindow_Loaded(object sender, RoutedEventArgs e)
+    {
+        var screen = WpfScreen.GetScreenFrom(new Point(Left, Top));
 
-            double spareheight = 90;
-            double sparewidth = 25;
+        double spareheight = 90;
+        double sparewidth = 25;
 
-            var desiredheight = Form.ContentHeight + spareheight;
-            var desiredwidth = Form.ContentWidth + sparewidth;
+        var desiredheight = Form.ContentHeight + spareheight;
+        var desiredwidth = Form.ContentWidth + sparewidth;
 
-            var maxheight = screen.WorkingArea.Height - 0;
-            Height = desiredheight > maxheight ? maxheight : desiredheight;
+        var maxheight = screen.WorkingArea.Height - 0;
+        Height = desiredheight > maxheight ? maxheight : desiredheight;
 
-            var maxwidth = screen.WorkingArea.Width - 0;
-            Width = desiredwidth > maxwidth ? maxwidth : desiredwidth;
+        var maxwidth = screen.WorkingArea.Width - 0;
+        Width = desiredwidth > maxwidth ? maxwidth : desiredwidth;
 
-            Left = screen.DeviceBounds.Left + (screen.DeviceBounds.Width - Width) / 2.0F;
-            Top = screen.DeviceBounds.Top + (screen.DeviceBounds.Height - Height) / 2.0F;
+        Left = screen.DeviceBounds.Left + (screen.DeviceBounds.Width - Width) / 2.0F;
+        Top = screen.DeviceBounds.Top + (screen.DeviceBounds.Height - Height) / 2.0F;
 
-            var scaption = Form.Items[0].GetType().GetCaption();
-            Title = "Edit " + scaption.SplitCamelCase();
+        var scaption = Form.Items[0].GetType().GetCaption();
+        Title = "Edit " + scaption.SplitCamelCase();
+    }
+
+    #region ISubPanel
+
+    public event ISubPanel.ClosedEvent? SubPanelClosed;
+
+    void ISubPanel.Shutdown(CancelEventArgs? cancel)
+    {
+        if (bChanged)
+        {
+            this.Focus();
+            var window = cancel is null
+                ? MessageWindow.NewYesNo($"You have unsaved changes: do you wish to save this {CoreUtils.Neatify(Form.Items[0].GetType().GetCaption())}?", "Save changes?")
+                : MessageWindow.NewYesNoCancel($"You have unsaved changes: do you wish to save this {CoreUtils.Neatify(Form.Items[0].GetType().GetCaption())}?", "Save changes?");
+            var result = window.Display().Result;
+            switch (result)
+            {
+                case MessageWindowResult.Yes:
+                    Result = true;
+                    Close();
+                    break;
+                case MessageWindowResult.No:
+                    Result = false;
+                    Close();
+                    break;
+                case MessageWindowResult.Cancel:
+                    if(cancel is not null)
+                    {
+                        cancel.Cancel = true;
+                    }
+                    return;
+            }
         }
     }
+
+    #endregion
 }

+ 69 - 0
inabox.wpf/Panel/IPanel.cs

@@ -255,4 +255,73 @@ public static class IPanelHostExtensions
     {
         host.CreateSetupActionIf(caption, image, onExecute, Security.CanView<T>(), menu);
     }
+}
+
+public interface ISubPanelHost
+{
+    List<ISubPanel> SubPanels { get; }
+
+    static ISubPanelHost Global = new GlobalSubPanelHost();
+
+    private class GlobalSubPanelHost : ISubPanelHost
+    {
+        public List<ISubPanel> SubPanels { get; set; } = new();
+    }
+}
+
+public static class ISubPanelHostExtensions
+{
+    public static void ShutdownSubPanels(this ISubPanelHost host, CancelEventArgs? cancel)
+    {
+        ISubPanel[] panels;
+        lock (host.SubPanels)
+        {
+            panels = host.SubPanels.ToArray();
+            host.SubPanels.Clear();
+        }
+
+        foreach(var panel in panels)
+        {
+            panel.Shutdown(cancel);
+            if (cancel?.Cancel == true)
+            {
+                return;
+            }
+        }
+    }
+
+    public static void AddSubPanel(this ISubPanelHost host, ISubPanel panel)
+    {
+        host.SubPanels.Add(panel);
+        panel.SubPanelClosed += p =>
+        {
+            lock (host.SubPanels)
+            {
+                host.SubPanels.Remove(p);
+            }
+        };
+    }
+}
+
+/// <summary>
+/// An <see cref="ISubPanel"/> is a non-modal window, which is tied to a parent 
+/// </summary>
+public interface ISubPanel
+{
+    public delegate void ClosedEvent(ISubPanel panel);
+
+    /// <summary>
+    /// Event to be called when a sub-panel closes itself; in this case, <see cref="Shutdown(CancelEventArgs?)"/> will not be called. This allows
+    /// the host to get rid of the sub-panel, instead of keeping it forever.
+    /// </summary>
+    /// <remarks>
+    /// You may call this after <see cref="Shutdown(CancelEventArgs?)"/> has been called.
+    /// </remarks>
+    public event ClosedEvent? SubPanelClosed;
+
+    /// <summary>
+    /// Shutdown the panel.
+    /// </summary>
+    /// <param name="cancel">If the operation can be cancelled, this is not <see langword="null"/></param>
+    void Shutdown(CancelEventArgs? cancel);
 }