Prompt - Generic Input Dialogs
Prompt<TView, TResult> enables any View to be shown in a modal dialog with Ok/Cancel buttons and typed result extraction. This provides a flexible, type-safe way to prompt users for input using any Terminal.Gui view.
Key Features
- Any View: Wrap any view (TextField, DatePicker, ColorPicker, ListView, etc.) in a dialog
- Type-Safe Results: Generic
TResultparameter provides compile-time type safety - Auto-Text Fallback: When
TResultisstring, automatically usesView.Textif no extractor provided - Fluent API: Extension methods on
IRunnableandIApplicationfor convenient usage - Cross-Language: Optimized APIs for both C# and PowerShell/scripting scenarios
- Customizable: Full control via
beginInitHandlercallback
Architecture
classDiagram
Dialog~TResult~ <|-- Prompt~TView,TResult~
IRunnable~TResult~ <|.. Prompt~TView,TResult~
class Dialog~TResult~ {
+TResult? Result
+Button[] Buttons
+OnAccepting(CommandEventArgs) bool
}
class Prompt~TView,TResult~ {
-TView _wrappedView
+Func~TView,TResult?~? ResultExtractor
+GetWrappedView() TView
#OnAccepting(CommandEventArgs) bool
}
note for Prompt~TView,TResult~ "Auto-uses View.Text when\nTResult is string and\nResultExtractor is null"
Basic Usage
C# - Typed Result Extraction
// From within a Window or other Runnable
DateTime? date = this.Prompt<DatePicker, DateTime> (
resultExtractor: dp => dp.Date,
beginInitHandler: prompt =>
{
prompt.Title = "Select Date";
prompt.GetWrappedView ().Date = DateTime.Now;
});
if (date is { } selectedDate)
{
MessageBox.Query ("Success", $"You selected: {selectedDate:yyyy-MM-dd}", Strings.btnOk);
}
C# - Auto-Text Extraction
When TResult is string, you can omit the resultExtractor:
// Auto-uses ColorPicker.Text (returns color name/value)
string? colorText = mainWindow.Prompt<ColorPicker, string> (
beginInitHandler: prompt =>
{
prompt.Title = "Pick a Color";
prompt.GetWrappedView ().SelectedColor = Color.Blue;
});
if (colorText is { })
{
MessageBox.Query ("Color", $"You selected: {colorText}", Strings.btnOk);
}
PowerShell - Simple Text Result
# Create and configure view
$textField = [TextField]::new()
$textField.Text = "Enter your name"
# Returns view.Text or null
$result = $app.Prompt($textField)
if ($result) {
Write-Output "User entered: $result"
}
PowerShell - Typed Result (Advanced)
# For non-text results, use the generic method
$datePicker = [DatePicker]::new()
$datePicker.Date = [DateTime]::Now
$result = $mainWindow.Prompt[DatePicker,DateTime](
$datePicker,
[Func[DatePicker,DateTime]] { param($dp) return $dp.Date })
if ($result) {
Write-Output $result.ToString("yyyy-MM-dd")
}
Extension Methods
IRunnable.Prompt (C# - Full Control)
public static TResult? Prompt<TView, TResult> (
this IRunnable host,
TView? view = null,
Func<TView, TResult?>? resultExtractor = null,
TResult? input = default,
Action<Prompt<TView, TResult>>? beginInitHandler = null)
where TView : View, new()
Parameters:
view: View instance to wrap, ornullto auto-createresultExtractor: Function to extract result from view, ornullfor auto-Text (whenTResultisstring)input: Initial value (reserved for futureIValue<T>pattern)beginInitHandler: Callback to customize dialog before display
Returns: TResult? - extracted result or null if canceled
Auto-Text Fallback: If resultExtractor is null and TResult is string, automatically returns view.Text.
IApplication.Prompt (PowerShell - Simple)
public static string? Prompt<TView> (
this IApplication app,
TView view)
where TView : View, new()
Parameters:
view: View instance to display
Returns: string? - view.Text or null if canceled
Note: For views where .Text is not meaningful (e.g., ListView with multi-select), use the generic IRunnable.Prompt method with a custom result extractor.
Usage Patterns
Pattern 1: Pre-Create View
Most common pattern - create and configure view, then prompt:
ColorPicker colorPicker = new ()
{
SelectedColor = Color.Red,
Style = new () { ShowColorName = true, ShowTextFields = true }
};
Color? result = mainWindow.Prompt<ColorPicker, Color> (
view: colorPicker,
resultExtractor: cp => cp.SelectedColor,
beginInitHandler: prompt => prompt.Title = "Pick Color");
Pattern 2: Auto-Create View
Let Prompt create the view, customize via handler:
DateTime? date = mainWindow.Prompt<DatePicker, DateTime> (
resultExtractor: dp => dp.Date,
beginInitHandler: prompt =>
{
prompt.Title = "Select Date";
prompt.GetWrappedView ().Date = DateTime.Now;
prompt.GetWrappedView ().Format = "yyyy-MM-dd";
});
Pattern 3: Auto-Text for String Results
When you want the string representation:
// Get date as formatted string instead of DateTime
string? dateText = mainWindow.Prompt<DatePicker, string> (
beginInitHandler: prompt =>
{
prompt.Title = "Select Date";
prompt.GetWrappedView ().Date = DateTime.Now;
prompt.GetWrappedView ().Format = "MM/dd/yyyy";
});
Pattern 4: Direct Construction (Maximum Control)
Create Prompt directly when you need full control:
TextField textField = new () { Width = Dim.Fill () };
using Prompt<TextField, string> prompt = new (textField)
{
Title = "Enter Name",
ResultExtractor = tf => tf.Text
};
// Customize via events
prompt.Initialized += (s, e) =>
{
prompt.BorderStyle = LineStyle.Rounded;
prompt.Buttons [0].Text = "Cancel";
prompt.Buttons [1].Text = "Submit";
};
string? result = app.Run (prompt);
View.Text Support
Many views provide meaningful .Text implementations that enable automatic text extraction:
| View | Text Behavior |
|---|---|
TextField |
User input text |
TextView |
Multiline text content |
DatePicker |
Formatted date string (e.g., "1/15/2026") |
ColorPicker |
Color name or representation (e.g., "Red", "Color [A=255, R=255, G=0, B=0]") |
Label |
Label text |
Button |
Button text |
Views where .Text is not meaningful:
ListView(especially with multi-select) - use customresultExtractorTreeView- use customresultExtractor- Custom views without
.Textoverride - use customresultExtractor
Customization
Customizing the Dialog
Use beginInitHandler to customize dialog properties:
Color? result = mainWindow.Prompt<ColorPicker, Color> (
resultExtractor: cp => cp.SelectedColor,
beginInitHandler: prompt =>
{
// Dialog properties
prompt.Title = "Choose Color";
prompt.BorderStyle = LineStyle.Rounded;
prompt.Width = Dim.Percent (60);
prompt.Height = Dim.Percent (50);
// Wrapped view properties
prompt.GetWrappedView ().SelectedColor = Color.Blue;
prompt.GetWrappedView ().Width = Dim.Fill ();
});
Customizing Buttons
Buttons are added in constructor. Customize via Initialized event:
DateTime? date = mainWindow.Prompt<DatePicker, DateTime> (
resultExtractor: dp => dp.Date,
beginInitHandler: prompt =>
{
prompt.Title = "Select Date";
prompt.Initialized += (s, e) =>
{
// Buttons[0] is Cancel, Buttons[1] is Ok
prompt.Buttons [0].Text = "Nope";
prompt.Buttons [1].Text = "Yep";
};
});
Result Extraction Logic
The OnAccepting method uses this logic:
protected override bool OnAccepting (CommandEventArgs args)
{
if (base.OnAccepting (args)) return true;
if (ResultExtractor is { })
{
// 1. Use explicit extractor if provided
Result = ResultExtractor (GetWrappedView ());
}
else if (typeof(TResult) == typeof(string))
{
// 2. Auto-fallback to .Text when TResult is string
Result = (TResult?)(object?)GetWrappedView ().Text;
}
// 3. Otherwise Result remains null
return false;
}
Priority:
- Explicit
ResultExtractor- always used if provided - Auto
.Textextraction - whenTResultisstringand no extractor - null - when no extractor and
TResultis notstring
PowerShell Considerations
Simple Text Input
For simple text input, the IApplication.Prompt method is ideal:
$textField = [TextField]::new()
$textField.Text = "Default value"
$result = $app.Prompt($textField) # Returns string? from view.Text
Typed Results
For non-text results, PowerShell users have two options:
Option 1: Use generic IRunnable method (requires Func delegate):
$result = $mainWindow.Prompt[DatePicker,DateTime](
$datePicker,
[Func[DatePicker,DateTime]] { param($dp) return $dp.Date })
Option 2: Create Prompt directly (more verbose but clearer):
$prompt = [Prompt[DatePicker,DateTime]]::new($datePicker)
$prompt.Title = "Select Date"
$prompt.ResultExtractor = [Func[DatePicker,DateTime]] {
param($dp)
return $dp.Date
}
$app.Run($prompt)
$result = $prompt.Result
Null Handling
Always use nullable result types to distinguish cancellation from valid results:
// GOOD - nullable type
DateTime? date = mainWindow.Prompt<DatePicker, DateTime?> (...);
if (date is null)
{
// User canceled
}
else
{
// User accepted with value
}
// BAD - non-nullable value type
DateTime date = mainWindow.Prompt<DatePicker, DateTime> (...);
// Returns default(DateTime) on cancel - can't distinguish from actual default value!
The IValue<T> Interface
Views that implement IValue<T> provide automatic result extraction without requiring an explicit resultExtractor. This is the recommended pattern for views that have a primary value.
Interface Definition
public interface IValue<TValue>
{
TValue? Value { get; set; }
event EventHandler<ValueChangingEventArgs<TValue?>>? ValueChanging;
event EventHandler<ValueChangedEventArgs<TValue?>>? ValueChanged;
}
The interface follows the Cancellable Work Pattern (CWP):
ValueChangingfires before the value changes; setHandled = trueto cancelValueChangedfires after the value has changed
Automatic Result Extraction
When the wrapped view implements IValue<TResult>, Prompt automatically extracts the result:
// No resultExtractor needed - AttributePicker implements IValue<Attribute?>
Attribute? attr = mainWindow.Prompt<AttributePicker, Attribute?> (
beginInitHandler: prompt =>
{
prompt.Title = "Select Text Attribute";
prompt.GetWrappedView ().Value = new Attribute (Color.White, Color.Blue);
});
Implementing IValue<T> in Custom Views
To make your custom view work seamlessly with Prompt, implement IValue<T> using CWPPropertyHelper.ChangeProperty:
public class MyRatingView : View, IValue<int?>
{
private int? _value;
public int? Value
{
get => _value;
set => CWPPropertyHelper.ChangeProperty (
this,
ref _value,
value,
OnValueChanging,
ValueChanging,
newValue =>
{
_value = newValue;
UpdateStarDisplay ();
},
OnValueChanged,
ValueChanged,
out _);
}
public event EventHandler<ValueChangingEventArgs<int?>>? ValueChanging;
public event EventHandler<ValueChangedEventArgs<int?>>? ValueChanged;
protected virtual bool OnValueChanging (ValueChangingEventArgs<int?> args) => false;
protected virtual void OnValueChanged (ValueChangedEventArgs<int?> args) { }
}
Now your view works with Prompt without a custom extractor:
int? rating = mainWindow.Prompt<MyRatingView, int?> (
beginInitHandler: prompt => prompt.Title = "Rate this item");
Example: AttributePicker
AttributePicker is a composite view that demonstrates the IValue<T> pattern. It allows users to select a complete Attribute (foreground color, background color, and text style).
Basic Usage
// Simple - IValue<Attribute?> provides automatic extraction
Attribute? result = mainWindow.Prompt<AttributePicker, Attribute?> (
beginInitHandler: prompt =>
{
prompt.Title = "Choose Text Style";
prompt.GetWrappedView ().SampleText = "Preview your selection";
});
if (result.HasValue)
{
myTextView.SetAttribute (result.Value);
}
With Initial Value and Validation
AttributePicker picker = new ()
{
Value = existingAttribute,
SampleText = "Sample Text Preview"
};
// Validate changes
picker.ValueChanging += (_, e) =>
{
// Prevent identical foreground/background
if (e.NewValue?.Foreground == e.NewValue?.Background)
{
e.Handled = true;
}
};
Attribute? result = mainWindow.Prompt<AttributePicker, Attribute?> (
view: picker,
beginInitHandler: prompt => prompt.Title = "Edit Attribute");
Future Enhancements
Hosted Modal Positioning (Planned)
The host parameter in IRunnable.Prompt captures the hosting relationship. Future versions will use this for relative positioning:
// Future: Dialog positioned relative to mainWindow, not just centered on screen
DateTime? date = mainWindow.Prompt<DatePicker, DateTime> (...);
See Also
- Dialog - Base class for modal dialogs
- Runnable - Session management and lifecycle
- Application Architecture - Understanding the IApplication pattern
- View - Base View class and Text property
- IValue<T> - Interface for views with typed values
- AttributePicker - Example IValue implementation
- Cancellable Work Pattern - CWP event pattern used by IValue