IDynamicEditorHost.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using InABox.Clients;
  5. using InABox.Core;
  6. namespace InABox.DynamicGrid;
  7. public interface IDynamicEditorHost
  8. {
  9. /// <summary>
  10. /// A list of columns which are defined for this editor; from this are loaded the additional columns for lookups. A useful default is just
  11. /// to call <see cref="DynamicGridColumns.ExtractColumns(Type)"/>, if a singular type for the editor is well-defined.
  12. /// </summary>
  13. /// <remarks>
  14. /// 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
  15. /// 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.
  16. /// </remarks>
  17. IEnumerable<DynamicGridColumn> Columns { get; }
  18. /// <summary>
  19. /// Loads into <paramref name="columns"/> all columns that start with the same prefix as <paramref name="column"/>; e.g, when taking a
  20. /// lookup defined for column EntityLink.ID, <paramref name="columns"/> will contain all EntityLink.* except EntityLink.ID.
  21. ///
  22. /// This essentially gives us the other columns we need to load from the database for lookups.
  23. /// See <see cref="DefaultDynamicEditorHost{T}.LoadColumns(string, Dictionary{string, string})"/> for the canonical implementation.
  24. /// </summary>
  25. /// <remarks>
  26. /// This is dumb; we don't want it, because the presence of <see cref="Columns"/> kinda makes it redundant.
  27. /// </remarks>
  28. /// <param name="column">The column to use the prefix of.</param>
  29. /// <param name="columns">The dictionary into which the columns will be loaded, in the form "FieldName": "EntityLink.FieldName"</param>
  30. void LoadColumns(string column, Dictionary<string, string> columns);
  31. /// <summary>
  32. /// In most cases, calls <see cref="LookupFactory.DefineFilter(Type)"/>, and I think this should explain what this method does. Provide a method that
  33. /// 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
  34. /// check <paramref name="type"/>, and use the <see cref="LookupFactory"/> function if not the type you want).
  35. /// </summary>
  36. /// <param name="type">The T in <see cref="Filter{T}"/>.</param>
  37. /// <returns>A filter (or <see langword="null"/>, being synonymous with <see cref="Filter{T}.All"/>).</returns>
  38. IFilter? DefineFilter(Type type);
  39. /// <summary>
  40. /// Trigger the loading of the lookup values; the canonical implementation calls <see cref="ILookupEditor.Values(string, object[]?)"/>,
  41. /// and calls (either sync/async) <see cref="ILookupEditorControl.LoadLookups(CoreTable)"/> when the values are loaded.
  42. /// </summary>
  43. /// <param name="editor">The editor to load the lookups for.</param>
  44. void LoadLookups(ILookupEditorControl editor);
  45. /// <summary>
  46. /// Get a document for a given filename.
  47. /// </summary>
  48. /// <remarks>
  49. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  50. /// </remarks>
  51. /// <param name="filename">The filename of the document.</param>
  52. /// <returns>The document with the right filename, or <see langword="null"/> if not found.</returns>
  53. Document? FindDocument(string filename)
  54. {
  55. return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
  56. }
  57. /// <summary>
  58. /// Get a document for a given ID.
  59. /// </summary>
  60. /// <remarks>
  61. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  62. /// </remarks>
  63. /// <param name="id">The ID of the document.</param>
  64. /// <returns>The document, or <see langword="null"/> if not found.</returns>
  65. Document? GetDocument(Guid id)
  66. {
  67. return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
  68. }
  69. /// <summary>
  70. /// Saves a document.
  71. /// </summary>
  72. /// <remarks>
  73. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  74. /// </remarks>
  75. /// <param name="document">The document to save.</param>
  76. void SaveDocument(Document document)
  77. {
  78. new Client<Document>().Save(document, "Updated by Editor");
  79. }
  80. /// <summary>
  81. /// Returns a list of the currently edited items; may be an empty array.
  82. ///
  83. /// This should probably always be a <see cref="BaseObject"/>[].
  84. /// </summary>
  85. /// <returns>The items being edited.</returns>
  86. object?[] GetItems();
  87. /// <summary>
  88. /// Um... I'm really not sure; achieves the same function as <see cref="DynamicEditorGrid.OnGetEditor"/> - if you know what that does, good job.
  89. /// </summary>
  90. /// <remarks>
  91. /// I think you should be fine to just return <paramref name="column"/>.Editor, as defined by <see cref="DynamicGridColumn"/>.
  92. /// </remarks>
  93. /// <param name="column">The column to get the editor of.</param>
  94. /// <returns>The editor, or <see langword="null"/> if it doesn't exist.</returns>
  95. BaseEditor? GetEditor(DynamicGridColumn column);
  96. }
  97. public class DefaultDynamicEditorHost<T> : IDynamicEditorHost
  98. where T : BaseObject
  99. {
  100. public virtual T[]? Items { get; set; }
  101. public IEnumerable<DynamicGridColumn> Columns => new DynamicGridColumns().ExtractColumns(typeof(T));
  102. public virtual IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(Items ?? Enumerable.Empty<object?>(), type);
  103. public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
  104. public object?[] GetItems() => Items ?? Array.Empty<object?>();
  105. public void LoadColumns(string column, Dictionary<string, string> columns)
  106. {
  107. columns.Clear();
  108. var comps = column.Split('.').ToList();
  109. comps.RemoveAt(comps.Count - 1);
  110. var prefix = string.Format("{0}.", string.Join(".", comps));
  111. var cols = Columns.Where(x => !x.ColumnName.Equals(column) && x.ColumnName.StartsWith(prefix));
  112. foreach (var col in cols)
  113. columns[col.ColumnName.Replace(prefix, "")] = col.ColumnName;
  114. }
  115. public void LoadLookups(ILookupEditorControl sender)
  116. {
  117. var editor = sender.EditorDefinition as ILookupEditor;
  118. var colname = sender.ColumnName;
  119. var values = editor.Values(colname, Items);
  120. sender.LoadLookups(values);
  121. }
  122. }