View is the base class for all visible UI elements in Terminal.Gui. View provides core functionality for layout, drawing, input handling, navigation, and scrolling. All interactive controls, windows, and dialogs derive from View.
See the Views Overview for a catalog of all built-in View subclasses.
App.Driver for convenience.Views are composed of several nested layers that define how they are positioned, drawn, and scrolled:
[!INCLUDE View Composition]
See the Layout Deep Dive for complete details on View composition and layout.
Viewport - The visible "window" into the View's content, located inside the adornments. Viewport coordinates are always relative to (0,0) of the content area
// Frame is SuperView-relative
view.Frame = new Rectangle(10, 5, 50, 20);
// Viewport is content-relative (the visible portal)
view.Viewport = new Rectangle(0, 0, 45, 15); // Adjusted for adornments
The Content Area is where the View's content is drawn. By default, the content area size matches the Viewport size. To enable scrolling:
Viewport.Location to scroll the contentSee the Scrolling Deep Dive for complete details.
Adornments are special Views that surround the content:
Each adornment has a Thickness that defines the width of each side (Top, Right, Bottom, Left).
See the Layout Deep Dive for complete details on adornments.
Views implement ISupportInitializeNotification:
During initialization, View.App is set to reference the application context, enabling views to access application services like the driver and current session.
Views are IDisposable:
View is organized as a partial class across multiple files, each handling a specific subsystem:
See the Command Deep Dive.
See the Keyboard Deep Dive.
KeyDown, KeyUp, InvokingKeyBindingsSee the Mouse Deep Dive.
MouseEnter, MouseLeave, MouseEventSee the Layout Deep Dive and Arrangement Deep Dive.
LayoutStarted - Before layout beginsLayoutComplete - After layout completesFrameChanged - When Frame changesViewportChanged - When Viewport changesSee the Drawing Deep Dive.
See the Scheme Deep Dive for details on color theming.
DrawingContent - Before content is drawnDrawingContentComplete - After content is drawnDrawingAdornments - Before adornments are drawnDrawingAdornmentsComplete - After adornments are drawnSee the Navigation Deep Dive.
Events:
HasFocusChanging - Before focus changes (cancellable)HasFocusChanged - After focus changesAccepting - When Command.Accept is invoked (typically Enter key)Accepted - After Command.Accept completesActivating - When Command.Activate is invoked (typically Space or mouse click)Activated - After Command.Activate completesSee the Scrolling Deep Dive.
View view = new ()
{
X = Pos.Center(),
Y = Pos.Center(),
Width = Dim.Percent(50),
Height = Dim.Fill()
};
When a View is added to a SuperView or when Application.Run is called:
Layout happens automatically when needed:
LayoutStarted event is raisedLayoutComplete event is raisedDrawing happens automatically when needed:
DrawingContent event is raisedDrawingContentComplete event is raisedInput is processed in this order:
view.Dispose();
See the Command Deep Dive for complete details.
Views use a command pattern for handling input:
// Add a command the view supports
view.AddCommand (Command.Accept, () =>
{
// Handle the Accept command
return true;
});
// Bind a key to the command
view.KeyBindings.Add (Key.Enter, Command.Accept);
// Bind a mouse action to the command
view.MouseBindings.Add (MouseFlags.Button1Clicked, Command.Activate);
See the Keyboard Deep Dive for complete details.
The keyboard subsystem processes key presses through:
See the Mouse Deep Dive for complete details.
The mouse subsystem processes mouse events through:
See the Layout Deep Dive for complete details.
Layout is declarative using Pos and Dim:
var label = new Label { Text = "Name:" };
var textField = new TextField
{
X = Pos.Right(label) + 1,
Y = Pos.Top(label),
Width = Dim.Fill()
};
The layout system automatically:
See the Drawing Deep Dive for complete details.
Views draw themselves using viewport-relative coordinates:
protected override bool OnDrawingContent()
{
// Draw at viewport coordinates (0,0)
Move(0, 0);
SetAttribute(new Attribute(Color.White, Color.Blue));
AddStr("Hello, Terminal.Gui!");
return true;
}
Key drawing concepts:
See the Navigation Deep Dive for complete details.
Navigation controls keyboard focus movement:
See the Scrolling Deep Dive for complete details.
Scrolling is built into every View:
// Set content size larger than viewport
view.SetContentSize(new Size(100, 100));
// Scroll the content
view.Viewport = view.Viewport with { Location = new Point(10, 10) };
// Or use helper methods
view.ScrollVertical(5);
view.ScrollHorizontal(3);
// Enable scrollbars
view.VerticalScrollBar.Visible = true;
view.HorizontalScrollBar.Visible = true;
public class MyCustomView : View
{
public MyCustomView()
{
// Set up default size
Width = Dim.Auto();
Height = Dim.Auto();
// Can receive focus
CanFocus = true;
// Add supported commands
AddCommand(Command.Accept, HandleAccept);
// Configure key bindings
KeyBindings.Add(Key.Enter, Command.Accept);
}
protected override bool OnDrawingContent()
{
// Draw custom content using viewport coordinates
Move(0, 0);
SetAttributeForRole(VisualRole.Normal);
AddStr("My custom content");
return true; // Handled
}
private bool HandleAccept()
{
// Handle the Accept command
// Raise events, update state, etc.
return true; // Handled
}
}
var container = new View
{
Width = Dim.Fill(),
Height = Dim.Fill()
};
var button1 = new Button { Text = "OK", X = 2, Y = 2 };
var button2 = new Button { Text = "Cancel", X = Pos.Right(button1) + 2, Y = 2 };
container.Add(button1, button2);
var view = new View
{
BorderStyle = LineStyle.Double,
Title = "My View"
};
// Configure border
view.Border.Thickness = new Thickness(1);
view.Border.Settings = BorderSettings.Title;
// Add padding
view.Padding.Thickness = new Thickness(1);
// Add margin
view.Margin.Thickness = new Thickness(2);
var view = new View
{
Width = 40,
Height = 20
};
// Set content larger than viewport
view.SetContentSize(new Size(100, 100));
// Enable scrollbars with auto-show
view.VerticalScrollBar.AutoShow = true;
view.HorizontalScrollBar.AutoShow = true;
// Add key bindings for scrolling
view.KeyBindings.Add(Key.CursorUp, Command.ScrollUp);
view.KeyBindings.Add(Key.CursorDown, Command.ScrollDown);
view.KeyBindings.Add(Key.CursorLeft, Command.ScrollLeft);
view.KeyBindings.Add(Key.CursorRight, Command.ScrollRight);
// Add command handlers
view.AddCommand(Command.ScrollUp, () => { view.ScrollVertical(-1); return true; });
view.AddCommand(Command.ScrollDown, () => { view.ScrollVertical(1); return true; });
Views can implement IRunnable to run as independent, blocking sessions with typed results. This decouples runnability from inheritance, allowing any View to participate in session management.
The IRunnable pattern provides:
IRunnable<TResult> instead of inheriting from RunnableTResult parameter for compile-time type safetyInit(), Run(), and Shutdown() for concise codeIsRunningChanging/Changed, IsModalChanging/ChangedDerive from Runnable or implement IRunnable:
public class ColorPickerDialog : Runnable<Color?>
{
private ColorPicker16 _colorPicker;
public ColorPickerDialog()
{
Title = "Select a Color";
_colorPicker = new ColorPicker16 { X = Pos.Center(), Y = 2 };
var okButton = new Button { Text = "OK", IsDefault = true };
okButton.Accepting += (s, e) => {
Result = _colorPicker.SelectedColor;
Application.RequestStop();
};
Add(_colorPicker, okButton);
}
}
The fluent API enables elegant, concise code with automatic disposal:
// Framework creates, runs, and disposes the runnable automatically
Color? result = Application.Create()
.Init()
.Run<ColorPickerDialog>()
.Shutdown() as Color?;
if (result is { })
{
Console.WriteLine($"Activated: {result}");
}
For more control over the lifecycle:
var app = Application.Create();
app.Init();
var dialog = new ColorPickerDialog();
app.Run(dialog);
// Extract result after Run returns
Color? result = dialog.Result;
// Caller is responsible for disposal
dialog.Dispose();
app.Shutdown();
"Whoever creates it, owns it":
Run<TRunnable>(): Framework creates → Framework disposes (in Shutdown())Run(IRunnable): Caller creates → Caller disposesExtract the result in OnIsRunningChanging when stopping:
protected override bool OnIsRunningChanging(bool oldIsRunning, bool newIsRunning)
{
if (!newIsRunning) // Stopping - extract result before disposal
{
Result = _colorPicker.SelectedColor;
// Optionally cancel stop (e.g., prompt to save)
if (HasUnsavedChanges())
{
return true; // Cancel stop
}
}
return base.OnIsRunningChanging(oldIsRunning, newIsRunning);
}
IsRunning - True when on the RunnableSessionStackIsModal - True when at the top of the stack (receiving all input)Result - The typed result value (set before stopping)IsRunningChanging - Cancellable event before added/removed from stackIsRunningChanged - Non-cancellable event after stack changeIsModalChanged - Non-cancellable event after modal state changeViews can run modally (exclusively capturing all input until closed). See Runnable for the legacy pattern.
Note: New code should use IRunnable<TResult> pattern (see above) for better type safety and lifecycle management.
var dialog = new Dialog
{
Title = "Confirmation",
Width = Dim.Percent(50),
Height = Dim.Percent(50)
};
// Add content...
var label = new Label { Text = "Are you sure?", X = Pos.Center(), Y = 1 };
dialog.Add(label);
// Run modally - blocks until closed
Application.Run(dialog);
// Dialog has been closed
dialog.Dispose();
Dialogs are Modal Windows centered on screen:
bool okPressed = false;
var ok = new Button { Text = "Ok" };
ok.Accepting += (s, e) => { okPressed = true; Application.RequestStop(); };
var cancel = new Button { Text = "Cancel" };
cancel.Accepting += (s, e) => Application.RequestStop();
var dialog = new Dialog
{
Title = "Quit",
Width = 50,
Height = 10
};
dialog.Add(new Label { Text = "Are you sure you want to quit?", X = Pos.Center(), Y = 2 });
dialog.AddButton(ok);
dialog.AddButton(cancel);
Application.Run(dialog);
if (okPressed)
{
// User clicked OK
}
Which displays:
╔═ Quit ═══════════════════════════════════════════╗
║ ║
║ Are you sure you want to quit? ║
║ ║
║ ║
║ ║
║ [ Ok ] [ Cancel ] ║
╚══════════════════════════════════════════════════╝
Wizards let users step through multiple pages:
var wizard = new Wizard { Title = "Setup Wizard" };
var step1 = new WizardStep { Title = "Welcome" };
step1.Add(new Label { Text = "Welcome to the wizard!", X = 1, Y = 1 });
var step2 = new WizardStep { Title = "Configuration" };
step2.Add(new TextField { X = 1, Y = 1, Width = 30 });
wizard.AddStep(step1);
wizard.AddStep(step2);
Application.Run(wizard);
View.Diagnostics - ViewDiagnosticFlags for debugging:
Ruler - Shows a ruler around the ViewDrawIndicator - Shows an animated indicator when drawingFramePadding - Highlights the Frame with colorView.ShadowStyle - ShadowStyle for drop shadows:
view.ShadowStyle = ShadowStyle.Transparent;