Create Custom Renderables

Build your own widgets by implementing IRenderable

When you need a widget that doesn't exist, implement IRenderable.

Implement IRenderable

To create a custom renderable, implement Measure() and Render().

private readonly string _text;
private readonly Style _style;
  
public Label(string text)
    : this(text, Color.Black, Color.Grey)
{
}
  
public Label(string text, Color foreground, Color background)
{
    _text = text;
    _style = new Style(foreground, background);
}
  
public Measurement Measure(RenderOptions options, int maxWidth)
{
    // Add 2 for padding (space on each side)
    var width = _text.Length + 2;
    return new Measurement(width, width);
}
  
public IEnumerable<Segment> Render(RenderOptions options, int maxWidth)
{
    // Render padded text with our style
    yield return new Segment($" {_text} ", _style);
}

Calculate Size Constraints

To report your widget's size requirements, return a Measurement from Measure(). The measurement specifies minimum and maximum width in console cells.

// Add 2 for padding (space on each side)
var width = _text.Length + 2;
return new Measurement(width, width);

Generate Segments

To produce output, yield Segment objects from Render(). Each segment contains text and an optional style.

// Render padded text with our style
yield return new Segment($" {_text} ", _style);

Apply Styles

To add colors and formatting, pass a Style when creating segments.

var success = new Label("OK", Color.White, Color.Green);
var warning = new Label("WARN", Color.Black, Color.Yellow);
var error = new Label("ERROR", Color.White, Color.Red);
  
AnsiConsole.Write(success);
AnsiConsole.Write(" ");
AnsiConsole.Write(warning);
AnsiConsole.Write(" ");
AnsiConsole.Write(error);
AnsiConsole.WriteLine();

Wrap Other Renderables

To create container widgets, accept IRenderable and delegate rendering.

private readonly IRenderable _label;
private readonly IRenderable _value;
  
public LabeledValue(IRenderable label, IRenderable value)
{
    _label = label;
    _value = value;
}
  
public Measurement Measure(RenderOptions options, int maxWidth)
{
    var labelMeasure = _label.Measure(options, maxWidth);
    var remaining = maxWidth - labelMeasure.Max - 1; // -1 for space
    var valueMeasure = _value.Measure(options, Math.Max(0, remaining));
  
    var min = labelMeasure.Min + 1 + valueMeasure.Min;
    var max = labelMeasure.Max + 1 + valueMeasure.Max;
    return new Measurement(min, max);
}
  
public IEnumerable<Segment> Render(RenderOptions options, int maxWidth)
{
    // Render label
    var labelMeasure = _label.Measure(options, maxWidth);
    foreach (var segment in _label.Render(options, labelMeasure.Max))
    {
        yield return segment;
    }
  
    // Space between label and value
    yield return new Segment(" ");
  
    // Render value with remaining width
    var remaining = maxWidth - labelMeasure.Max - 1;
    foreach (var segment in _value.Render(options, Math.Max(0, remaining)))
    {
        yield return segment;
    }
}

See Also