Browse Source

Extracted DynamicGrid drag-drop into DynamicGridUtils

Kenric Nugteren 1 year ago
parent
commit
be0372d76d

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

@@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Data;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Linq;
 using System.Linq.Expressions;
@@ -22,6 +23,7 @@ using InABox.Clients;
 using InABox.Core;
 using InABox.Wpf;
 using InABox.WPF;
+using javax.xml.crypto;
 using Microsoft.CodeAnalysis.CSharp.Syntax;
 using Microsoft.Xaml.Behaviors;
 using Syncfusion.Data;
@@ -3408,8 +3410,6 @@ namespace InABox.DynamicGrid
 
         #region Drag + Drop
 
-        private static string DragFormat => typeof(DynamicGridDragFormat).FullName ?? "";
-
         /// <summary>
         /// Handle a number of rows from a different <see cref="DynamicGrid{T}"/> being dragged into this one.
         /// </summary>
@@ -3428,41 +3428,13 @@ namespace InABox.DynamicGrid
 
         private void DataGrid_Drop(object sender, DragEventArgs e)
         {
-            Logger.Send(LogType.Information,"","DataGrid_Drop");
-            
             if (!HasOption(DynamicGridOption.DragTarget))
                 return;
-            
-            Logger.Send(LogType.Information,"","DataGrid_Drop::DragTarget==true");
-            
-            if (e.Data.GetDataPresent(DragFormat))
-            {
-                Logger.Send(LogType.Information,"","DataGrid_Drop::DataPresent==true");
-                var data = e.Data.GetData(DragFormat) as DynamicGridDragFormat;
-                if (data is not null)
-                { 
-                    Logger.Send(LogType.Information,"","DataGrid_Drop::DragData==DynamicGridDragFormat");
-                    var table = new CoreTable();
-                    foreach (var column in data.Table.Columns)
-                    {
-                        if (column is DataColumn dataColumn)
-                        {
-                            table.Columns.Add(new CoreColumn { ColumnName = dataColumn.ColumnName.Replace('_', '.'), DataType = dataColumn.DataType });
-                        }
-                    }
-                    foreach (var row in data.Table.Rows)
-                    {
-                        if (row is DataRow dataRow)
-                        {
-                            var coreRow = table.NewRow();
-                            coreRow.LoadValues(dataRow.ItemArray);
-                            table.Rows.Add(coreRow);
-                        }
-                    }
 
-                    OnDragEnd(data.Entity, table, e);
-                    DoChanged();
-                }
+            if(DynamicGridUtils.TryGetDropData(e, out var entityType, out var table))
+            {
+                OnDragEnd(entityType, table, e);
+                DoChanged();
             }
             else
             {
@@ -3489,16 +3461,16 @@ namespace InABox.DynamicGrid
 
         protected DragDropEffects DragTable(Type entity, CoreTable table)
         {
-            Logger.Send(LogType.Information,"","DragTable");
+            Logger.Send(LogType.Information, "", "DragTable");
             var data = new DataObject();
-            data.SetData(DragFormat, new DynamicGridDragFormat(table.ToDataTable(), entity));
+            data.SetData(DynamicGridUtils.DragFormat, new DynamicGridDragFormat(table.ToDataTable(), entity));
             var effect = DragDrop.DoDragDrop(this, data, DragDropEffects.All);
             return effect;
         }
 
         protected virtual DragDropEffects OnRowsDragStart(CoreRow[] rows)
         {
-            Logger.Send(LogType.Information,"","OnRowsDragStart");
+            Logger.Send(LogType.Information, "", "OnRowsDragStart");
             var table = new CoreTable();
             table.LoadColumns(Data.Columns);
             table.LoadRows(rows);
@@ -3507,7 +3479,7 @@ namespace InABox.DynamicGrid
 
         private void RowDragDropController_DragStart(object? sender, GridRowDragStartEventArgs e)
         {
-            Logger.Send(LogType.Information,"","RowDragDropController_DragStart");
+            Logger.Send(LogType.Information, "", "RowDragDropController_DragStart");
             
             if (!HasOption(DynamicGridOption.DragSource))
                 return;

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

@@ -13,6 +13,7 @@ using InABox.Wpf;
 using InABox.Core.Reports;
 using Syncfusion.Data.Extensions;
 using System.Diagnostics.CodeAnalysis;
+using System.Data;
 
 namespace InABox.DynamicGrid
 {
@@ -517,6 +518,54 @@ namespace InABox.DynamicGrid
 
         #endregion
 
+        #region Drag + Drop
+
+        public static string DragFormat => typeof(DynamicGridDragFormat).FullName ?? "";
+
+        /// <summary>
+        /// Try to get data dragged from a <see cref="DynamicGrid{T}"/> from a <see cref="DragEventArgs"/>, returning <see langword="true"/>
+        /// if data was present.
+        /// </summary>
+        /// <param name="e"></param>
+        public static bool TryGetDropData(
+            DragEventArgs e,
+            [NotNullWhen(true)] out Type? type,
+            [NotNullWhen(true)] out CoreTable? table)
+        {
+            if (e.Data.GetDataPresent(DragFormat))
+            {
+                var data = e.Data.GetData(DragFormat) as DynamicGridDragFormat;
+                if (data is not null)
+                {
+                    table = new CoreTable();
+                    foreach (var column in data.Table.Columns)
+                    {
+                        if (column is DataColumn dataColumn)
+                        {
+                            table.Columns.Add(new CoreColumn { ColumnName = dataColumn.ColumnName.Replace('_', '.'), DataType = dataColumn.DataType });
+                        }
+                    }
+                    foreach (var row in data.Table.Rows)
+                    {
+                        if (row is DataRow dataRow)
+                        {
+                            var coreRow = table.NewRow();
+                            coreRow.LoadValues(dataRow.ItemArray);
+                            table.Rows.Add(coreRow);
+                        }
+                    }
+
+                    type = data.Entity;
+                    return true;
+                }
+            }
+            table = null;
+            type = null;
+            return false;
+        }
+
+        #endregion
+
         public static void PopulateFormMenu<TEntityForm, TEntity, TEntityLink>(
             ItemsControl menu,
             Guid entityID,

+ 120 - 121
inabox.wpf/DynamicGrid/IDynamicEditorHost.cs

@@ -4,139 +4,138 @@ using System.Linq;
 using InABox.Clients;
 using InABox.Core;
 
-namespace InABox.DynamicGrid
+namespace InABox.DynamicGrid;
+
+public interface IDynamicEditorHost
 {
-    public interface IDynamicEditorHost
+    /// <summary>
+    /// A list of columns which are defined for this editor; from this are loaded the additional columns for lookups. A useful default is just
+    /// to call <see cref="DynamicGridColumns.ExtractColumns(Type)"/>, if a singular type for the editor is well-defined.
+    /// </summary>
+    /// <remarks>
+    /// I'm still not sure whether this one is actually a good idea, but it seems to be how the editors have functioned for a while. My reasoning is that
+    /// if the lookup defines a column to be loaded, but it doesn't get loaded because it is not in this list, then we would have broken functionality.
+    /// </remarks>
+    IEnumerable<DynamicGridColumn> Columns { get; }
+
+    /// <summary>
+    /// Loads into <paramref name="columns"/> all columns that start with the same prefix as <paramref name="column"/>; e.g, when taking a
+    /// lookup defined for column EntityLink.ID, <paramref name="columns"/> will contain all EntityLink.* except EntityLink.ID.
+    /// 
+    /// This essentially gives us the other columns we need to load from the database for lookups.
+    /// See <see cref="DefaultDynamicEditorHost{T}.LoadColumns(string, Dictionary{string, string})"/> for the canonical implementation.
+    /// </summary>
+    /// <remarks>
+    /// This is dumb; we don't want it, because the presence of <see cref="Columns"/> kinda makes it redundant.
+    /// </remarks>
+    /// <param name="column">The column to use the prefix of.</param>
+    /// <param name="columns">The dictionary into which the columns will be loaded, in the form "FieldName": "EntityLink.FieldName"</param>
+    void LoadColumns(string column, Dictionary<string, string> columns);
+
+    /// <summary>
+    /// In most cases, calls <see cref="LookupFactory.DefineFilter(Type)"/>, and I think this should explain what this method does. Provide a method that
+    /// doesn't do this if you like breaking things (or if you need to provide a filter that differs from the standard lookup; just make sure that you
+    /// check <paramref name="type"/>, and use the <see cref="LookupFactory"/> function if not the type you want).
+    /// </summary>
+    /// <param name="type">The T in <see cref="Filter{T}"/>.</param>
+    /// <returns>A filter (or <see langword="null"/>, being synonymous with <see cref="Filter{T}.All"/>).</returns>
+    IFilter? DefineFilter(Type type);
+
+    /// <summary>
+    /// Trigger the loading of the lookup values; the canonical implementation calls <see cref="ILookupEditor.Values(string, object[]?)"/>,
+    /// and calls (either sync/async) <see cref="ILookupEditorControl.LoadLookups(CoreTable)"/> when the values are loaded.
+    /// </summary>
+    /// <param name="editor">The editor to load the lookups for.</param>
+    void LoadLookups(ILookupEditorControl editor);
+
+    /// <summary>
+    /// Get a document for a given filename.
+    /// </summary>
+    /// <remarks>
+    /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
+    /// </remarks>
+    /// <param name="filename">The filename of the document.</param>
+    /// <returns>The document with the right filename, or <see langword="null"/> if not found.</returns>
+    Document? FindDocument(string filename)
     {
-        /// <summary>
-        /// A list of columns which are defined for this editor; from this are loaded the additional columns for lookups. A useful default is just
-        /// to call <see cref="DynamicGridColumns.ExtractColumns(Type)"/>, if a singular type for the editor is well-defined.
-        /// </summary>
-        /// <remarks>
-        /// I'm still not sure whether this one is actually a good idea, but it seems to be how the editors have functioned for a while. My reasoning is that
-        /// if the lookup defines a column to be loaded, but it doesn't get loaded because it is not in this list, then we would have broken functionality.
-        /// </remarks>
-        IEnumerable<DynamicGridColumn> Columns { get; }
-
-        /// <summary>
-        /// Loads into <paramref name="columns"/> all columns that start with the same prefix as <paramref name="column"/>; e.g, when taking a
-        /// lookup defined for column EntityLink.ID, <paramref name="columns"/> will contain all EntityLink.* except EntityLink.ID.
-        /// 
-        /// This essentially gives us the other columns we need to load from the database for lookups.
-        /// See <see cref="DefaultDynamicEditorHost{T}.LoadColumns(string, Dictionary{string, string})"/> for the canonical implementation.
-        /// </summary>
-        /// <remarks>
-        /// This is dumb; we don't want it, because the presence of <see cref="Columns"/> kinda makes it redundant.
-        /// </remarks>
-        /// <param name="column">The column to use the prefix of.</param>
-        /// <param name="columns">The dictionary into which the columns will be loaded, in the form "FieldName": "EntityLink.FieldName"</param>
-        void LoadColumns(string column, Dictionary<string, string> columns);
-
-        /// <summary>
-        /// In most cases, calls <see cref="LookupFactory.DefineFilter(Type)"/>, and I think this should explain what this method does. Provide a method that
-        /// doesn't do this if you like breaking things (or if you need to provide a filter that differs from the standard lookup; just make sure that you
-        /// check <paramref name="type"/>, and use the <see cref="LookupFactory"/> function if not the type you want).
-        /// </summary>
-        /// <param name="type">The T in <see cref="Filter{T}"/>.</param>
-        /// <returns>A filter (or <see langword="null"/>, being synonymous with <see cref="Filter{T}.All"/>).</returns>
-        IFilter? DefineFilter(Type type);
-
-        /// <summary>
-        /// Trigger the loading of the lookup values; the canonical implementation calls <see cref="ILookupEditor.Values(string, object[]?)"/>,
-        /// and calls (either sync/async) <see cref="ILookupEditorControl.LoadLookups(CoreTable)"/> when the values are loaded.
-        /// </summary>
-        /// <param name="editor">The editor to load the lookups for.</param>
-        void LoadLookups(ILookupEditorControl editor);
-
-        /// <summary>
-        /// Get a document for a given filename.
-        /// </summary>
-        /// <remarks>
-        /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
-        /// </remarks>
-        /// <param name="filename">The filename of the document.</param>
-        /// <returns>The document with the right filename, or <see langword="null"/> if not found.</returns>
-        Document? FindDocument(string filename)
-        {
-            return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
-        }
-
-        /// <summary>
-        /// Get a document for a given ID.
-        /// </summary>
-        /// <remarks>
-        /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
-        /// </remarks>
-        /// <param name="id">The ID of the document.</param>
-        /// <returns>The document, or <see langword="null"/> if not found.</returns>
-        Document? GetDocument(Guid id)
-        {
-            return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
-        }
-
-        /// <summary>
-        /// Saves a document.
-        /// </summary>
-        /// <remarks>
-        /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
-        /// </remarks>
-        /// <param name="document">The document to save.</param>
-        void SaveDocument(Document document)
-        {
-            new Client<Document>().Save(document, "Updated by Editor");
-        }
-
-        /// <summary>
-        /// Returns a list of the currently edited items; may be an empty array.
-        /// 
-        /// This should probably always be a <see cref="BaseObject"/>[].
-        /// </summary>
-        /// <returns>The items being edited.</returns>
-        object?[] GetItems();
-
-        /// <summary>
-        /// Um... I'm really not sure; achieves the same function as <see cref="DynamicEditorGrid.OnGetEditor"/> - if you know what that does, good job.
-        /// </summary>
-        /// <remarks>
-        /// I think you should be fine to just return <paramref name="column"/>.Editor, as defined by <see cref="DynamicGridColumn"/>.
-        /// </remarks>
-        /// <param name="column">The column to get the editor of.</param>
-        /// <returns>The editor, or <see langword="null"/> if it doesn't exist.</returns>
-        BaseEditor? GetEditor(DynamicGridColumn column);
+        return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
     }
 
-    public class DefaultDynamicEditorHost<T> : IDynamicEditorHost
-        where T : BaseObject
+    /// <summary>
+    /// Get a document for a given ID.
+    /// </summary>
+    /// <remarks>
+    /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
+    /// </remarks>
+    /// <param name="id">The ID of the document.</param>
+    /// <returns>The document, or <see langword="null"/> if not found.</returns>
+    Document? GetDocument(Guid id)
     {
-        public virtual T[]? Items { get; set; }
+        return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
+    }
+
+    /// <summary>
+    /// Saves a document.
+    /// </summary>
+    /// <remarks>
+    /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
+    /// </remarks>
+    /// <param name="document">The document to save.</param>
+    void SaveDocument(Document document)
+    {
+        new Client<Document>().Save(document, "Updated by Editor");
+    }
 
-        public IEnumerable<DynamicGridColumn> Columns => new DynamicGridColumns().ExtractColumns(typeof(T));
+    /// <summary>
+    /// Returns a list of the currently edited items; may be an empty array.
+    /// 
+    /// This should probably always be a <see cref="BaseObject"/>[].
+    /// </summary>
+    /// <returns>The items being edited.</returns>
+    object?[] GetItems();
+
+    /// <summary>
+    /// Um... I'm really not sure; achieves the same function as <see cref="DynamicEditorGrid.OnGetEditor"/> - if you know what that does, good job.
+    /// </summary>
+    /// <remarks>
+    /// I think you should be fine to just return <paramref name="column"/>.Editor, as defined by <see cref="DynamicGridColumn"/>.
+    /// </remarks>
+    /// <param name="column">The column to get the editor of.</param>
+    /// <returns>The editor, or <see langword="null"/> if it doesn't exist.</returns>
+    BaseEditor? GetEditor(DynamicGridColumn column);
+}
+
+public class DefaultDynamicEditorHost<T> : IDynamicEditorHost
+    where T : BaseObject
+{
+    public virtual T[]? Items { get; set; }
 
-        public virtual IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(Items ?? Enumerable.Empty<object?>(), type);
+    public IEnumerable<DynamicGridColumn> Columns => new DynamicGridColumns().ExtractColumns(typeof(T));
 
-        public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
+    public virtual IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(Items ?? Enumerable.Empty<object?>(), type);
 
-        public object?[] GetItems() => Items ?? Array.Empty<object?>();
+    public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
 
-        public void LoadColumns(string column, Dictionary<string, string> columns)
-        {
-            columns.Clear();
-            var comps = column.Split('.').ToList();
-            comps.RemoveAt(comps.Count - 1);
-            var prefix = string.Format("{0}.", string.Join(".", comps));
-            var cols = Columns.Where(x => !x.ColumnName.Equals(column) && x.ColumnName.StartsWith(prefix));
-            foreach (var col in cols)
-                columns[col.ColumnName.Replace(prefix, "")] = col.ColumnName;
-        }
+    public object?[] GetItems() => Items ?? Array.Empty<object?>();
 
-        public void LoadLookups(ILookupEditorControl sender)
-        {
-            var editor = sender.EditorDefinition as ILookupEditor;
-            var colname = sender.ColumnName;
+    public void LoadColumns(string column, Dictionary<string, string> columns)
+    {
+        columns.Clear();
+        var comps = column.Split('.').ToList();
+        comps.RemoveAt(comps.Count - 1);
+        var prefix = string.Format("{0}.", string.Join(".", comps));
+        var cols = Columns.Where(x => !x.ColumnName.Equals(column) && x.ColumnName.StartsWith(prefix));
+        foreach (var col in cols)
+            columns[col.ColumnName.Replace(prefix, "")] = col.ColumnName;
+    }
 
-            var values = editor.Values(colname, Items);
-            sender.LoadLookups(values);
-        }
+    public void LoadLookups(ILookupEditorControl sender)
+    {
+        var editor = sender.EditorDefinition as ILookupEditor;
+        var colname = sender.ColumnName;
 
+        var values = editor.Values(colname, Items);
+        sender.LoadLookups(values);
     }
+
 }