|
@@ -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);
|
|
|
}
|
|
|
+
|
|
|
}
|