IDynamicEditorHost.cs 6.8 KB

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