IDynamicEditorHost.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. /// Trigger the loading of the lookup values; the canonical implementation calls <see cref="ILookupEditor.Values(string, object[]?)"/>,
  33. /// and calls (either sync/async) <see cref="ILookupEditorControl.LoadLookups(CoreTable)"/> when the values are loaded.
  34. /// </summary>
  35. /// <param name="editor">The editor to load the lookups for.</param>
  36. void LoadLookups(ILookupEditorControl editor);
  37. /// <summary>
  38. /// Get a document for a given filename.
  39. /// </summary>
  40. /// <remarks>
  41. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  42. /// </remarks>
  43. /// <param name="filename">The filename of the document.</param>
  44. /// <returns>The document with the right filename, or <see langword="null"/> if not found.</returns>
  45. Document? FindDocument(string filename)
  46. {
  47. return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
  48. }
  49. /// <summary>
  50. /// Get a document for a given ID.
  51. /// </summary>
  52. /// <remarks>
  53. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  54. /// </remarks>
  55. /// <param name="id">The ID of the document.</param>
  56. /// <returns>The document, or <see langword="null"/> if not found.</returns>
  57. Document? GetDocument(Guid id)
  58. {
  59. return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
  60. }
  61. /// <summary>
  62. /// Saves a document.
  63. /// </summary>
  64. /// <remarks>
  65. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  66. /// </remarks>
  67. /// <param name="document">The document to save.</param>
  68. void SaveDocument(Document document)
  69. {
  70. new Client<Document>().Save(document, "Updated by Editor");
  71. }
  72. /// <summary>
  73. /// Returns a list of the currently edited items; may be an empty array.
  74. ///
  75. /// This should probably always be a <see cref="BaseObject"/>[].
  76. /// </summary>
  77. /// <returns>The items being edited.</returns>
  78. BaseObject[] GetItems();
  79. /// <summary>
  80. /// Return the type of the edited object.
  81. /// </summary>
  82. /// <returns></returns>
  83. Type GetEditorType();
  84. /// <summary>
  85. /// Um... I'm really not sure; achieves the same function as <see cref="DynamicEditorGrid.OnGetEditor"/> - if you know what that does, good job.
  86. /// </summary>
  87. /// <remarks>
  88. /// I think you should be fine to just return <paramref name="column"/>.Editor, as defined by <see cref="DynamicGridColumn"/>.
  89. /// </remarks>
  90. /// <param name="column">The column to get the editor of.</param>
  91. /// <returns>The editor, or <see langword="null"/> if it doesn't exist.</returns>
  92. BaseEditor? GetEditor(DynamicGridColumn column);
  93. }
  94. public class DefaultDynamicEditorHost<T> : IDynamicEditorHost
  95. where T : BaseObject
  96. {
  97. public virtual T[]? Items { get; set; }
  98. public IEnumerable<DynamicGridColumn> Columns => new DynamicGridColumns().ExtractColumns(typeof(T));
  99. public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
  100. public BaseObject[] GetItems() => Items ?? Array.Empty<BaseObject>();
  101. public Type GetEditorType() => typeof(T);
  102. public void LoadColumns(string column, Dictionary<string, string> columns)
  103. {
  104. columns.Clear();
  105. var comps = column.Split('.').ToList();
  106. comps.RemoveAt(comps.Count - 1);
  107. var prefix = string.Format("{0}.", string.Join(".", comps));
  108. var cols = Columns.Where(x => !x.ColumnName.Equals(column) && x.ColumnName.StartsWith(prefix));
  109. foreach (var col in cols)
  110. columns[col.ColumnName.Replace(prefix, "")] = col.ColumnName;
  111. }
  112. public void LoadLookups(ILookupEditorControl sender)
  113. {
  114. var editor = sender.EditorDefinition as ILookupEditor;
  115. var colname = sender.ColumnName;
  116. var values = editor.Values(typeof(T), colname, Items);
  117. sender.LoadLookups(values);
  118. }
  119. }