RunnableWrapper.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. namespace Terminal.Gui.ViewBase;
  2. /// <summary>
  3. /// Wraps any <see cref="View"/> to make it runnable with a typed result, similar to how
  4. /// <see cref="FlagSelector{TFlagsEnum}"/> wraps <see cref="FlagSelector"/>.
  5. /// </summary>
  6. /// <typeparam name="TView">The type of view being wrapped.</typeparam>
  7. /// <typeparam name="TResult">The type of result data returned when the session completes.</typeparam>
  8. /// <remarks>
  9. /// <para>
  10. /// This class enables any View to be run as a blocking session with <see cref="IApplication.Run"/>
  11. /// without requiring the View to implement <see cref="IRunnable{TResult}"/> or derive from
  12. /// <see cref="Runnable{TResult}"/>.
  13. /// </para>
  14. /// <para>
  15. /// Use <see cref="ViewRunnableExtensions.AsRunnable{TView, TResult}"/> for a fluent API approach,
  16. /// or <see cref="ApplicationRunnableExtensions.RunView{TView, TResult}"/> to run directly.
  17. /// </para>
  18. /// <example>
  19. /// <code>
  20. /// // Wrap a TextField to make it runnable with string result
  21. /// var textField = new TextField { Width = 40 };
  22. /// var runnable = new RunnableWrapper&lt;TextField, string&gt; { WrappedView = textField };
  23. ///
  24. /// // Extract result when stopping
  25. /// runnable.IsRunningChanging += (s, e) =&gt;
  26. /// {
  27. /// if (!e.NewValue) // Stopping
  28. /// {
  29. /// runnable.Result = runnable.WrappedView.Text;
  30. /// }
  31. /// };
  32. ///
  33. /// app.Run(runnable);
  34. /// Console.WriteLine($"User entered: {runnable.Result}");
  35. /// runnable.Dispose();
  36. /// </code>
  37. /// </example>
  38. /// </remarks>
  39. public class RunnableWrapper<TView, TResult> : Runnable<TResult> where TView : View
  40. {
  41. /// <summary>
  42. /// Initializes a new instance of <see cref="RunnableWrapper{TView, TResult}"/>.
  43. /// </summary>
  44. public RunnableWrapper ()
  45. {
  46. // Make the wrapper automatically size to fit the wrapped view
  47. Width = Dim.Fill ();
  48. Height = Dim.Fill ();
  49. }
  50. private TView? _wrappedView;
  51. /// <summary>
  52. /// Gets or sets the wrapped view that is being made runnable.
  53. /// </summary>
  54. /// <remarks>
  55. /// <para>
  56. /// This property must be set before the wrapper is initialized.
  57. /// Access this property to interact with the original view, extract its state,
  58. /// or configure result extraction logic.
  59. /// </para>
  60. /// </remarks>
  61. /// <exception cref="InvalidOperationException">Thrown if the property is set after initialization.</exception>
  62. public required TView WrappedView
  63. {
  64. get => _wrappedView ?? throw new InvalidOperationException ("WrappedView must be set before use.");
  65. init
  66. {
  67. if (IsInitialized)
  68. {
  69. throw new InvalidOperationException ("WrappedView cannot be changed after initialization.");
  70. }
  71. _wrappedView = value;
  72. }
  73. }
  74. /// <inheritdoc/>
  75. public override void EndInit ()
  76. {
  77. base.EndInit ();
  78. // Add the wrapped view as a subview after initialization
  79. if (_wrappedView is { })
  80. {
  81. Add (_wrappedView);
  82. }
  83. }
  84. }