IDynamicEditorHost.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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. /// <summary>
  56. /// Get a document for a given ID.
  57. /// </summary>
  58. /// <remarks>
  59. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  60. /// </remarks>
  61. /// <param name="id">The ID of the document.</param>
  62. /// <returns>The document, or <see langword="null"/> if not found.</returns>
  63. Document? GetDocument(Guid id);
  64. /// <summary>
  65. /// Saves a document.
  66. /// </summary>
  67. /// <remarks>
  68. /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
  69. /// </remarks>
  70. /// <param name="document">The document to save.</param>
  71. void SaveDocument(Document document);
  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. object?[] GetItems();
  79. /// <summary>
  80. /// Um... I'm really not sure; achieves the same function as <see cref="DynamicEditorGrid.OnGetEditor"/> - if you know what that does, good job.
  81. /// </summary>
  82. /// <remarks>
  83. /// I think you should be fine to just return <paramref name="column"/>.Editor, as defined by <see cref="DynamicGridColumn"/>.
  84. /// </remarks>
  85. /// <param name="column">The column to get the editor of.</param>
  86. /// <returns>The editor, or <see langword="null"/> if it doesn't exist.</returns>
  87. BaseEditor? GetEditor(DynamicGridColumn column);
  88. }
  89. public class DefaultDynamicEditorHost<T> : IDynamicEditorHost
  90. where T : BaseObject
  91. {
  92. public virtual T[]? Items { get; set; }
  93. public IEnumerable<DynamicGridColumn> Columns => new DynamicGridColumns().ExtractColumns(typeof(T));
  94. public virtual IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(Items ?? Enumerable.Empty<object?>(), type);
  95. public Document? FindDocument(string filename)
  96. {
  97. return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
  98. }
  99. public Document? GetDocument(Guid id)
  100. {
  101. Document? doc = null;
  102. if (id != Guid.Empty)
  103. doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
  104. return doc;
  105. }
  106. public void SaveDocument(Document document)
  107. {
  108. new Client<Document>().Save(document, "");
  109. }
  110. public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
  111. public object?[] GetItems() => Items ?? Array.Empty<object?>();
  112. public void LoadColumns(string column, Dictionary<string, string> columns)
  113. {
  114. columns.Clear();
  115. var comps = column.Split('.').ToList();
  116. comps.RemoveAt(comps.Count - 1);
  117. var prefix = string.Format("{0}.", string.Join(".", comps));
  118. var cols = Columns.Where(x => !x.ColumnName.Equals(column) && x.ColumnName.StartsWith(prefix));
  119. foreach (var col in cols)
  120. columns[col.ColumnName.Replace(prefix, "")] = col.ColumnName;
  121. }
  122. public void LoadLookups(ILookupEditorControl sender)
  123. {
  124. var editor = sender.EditorDefinition as ILookupEditor;
  125. var colname = sender.ColumnName;
  126. var values = editor.Values(colname, Items);
  127. sender.LoadLookups(values);
  128. }
  129. }
  130. }