Trace Chart

Distributed-trace waterfall of nested spans on a shared time axis.

Overview

The TraceChart renders a waterfall of TraceSpan rows, modeled on the spans of a distributed trace. Each span is drawn as one row with an indented name and a duration bar positioned on a shared time axis — offset by StartMs and sized by DurationMs. Nested spans supplied via child <TraceSpan> elements are flattened and indented by depth. Bar edges use eighth-blocks for sub-cell precision. Provide spans inline or bind SpansSource.

<TraceChart Title="GET /api/order/42" LabelWidth="18">
    <TraceSpan Name="http.request" StartMs="0" DurationMs="120">
        <TraceSpan Name="auth" StartMs="2" DurationMs="15" />
        <TraceSpan Name="db.query" StartMs="20" DurationMs="60">
            <TraceSpan Name="connect" StartMs="20" DurationMs="8" />
            <TraceSpan Name="exec" StartMs="30" DurationMs="45" />
        </TraceSpan>
        <TraceSpan Name="serialize" StartMs="85" DurationMs="30" />
    </TraceSpan>
</TraceChart>

Properties

PropertyTypeDescription
SpansIList<TraceSpan>Inline span collection (content property).
SpansSourceIEnumerableBound span collection; overrides Spans.
LabelWidthintWidth (cells) of the left gutter holding span names.
RowSpacingintBlank rows inserted between consecutive spans (default 1).
SelectedIndexintIndex of the selected row in the flattened span list (-1 = none). Two-way bindable.
SelectedSpanobjectThe selected span, kept in sync with SelectedIndex. Two-way bindable.
SelectedBackground / SelectedForegroundColorColors of the highlighted selected row.

TraceSpan fields: Name, StartMs, DurationMs, Depth, Color, and nested Children.

Data Binding

Bind a collection of spans from a ViewModel to SpansSource. The chart subscribes to INotifyCollectionChanged, so streaming spans in as a trace completes re-renders the waterfall automatically. Data classes and ViewModels must be public or internal so the AOT source generator can bind them.

public sealed class TraceViewModel : ViewModelBase
{
    public ObservableCollection<TraceSpan> Spans { get; } =
    [
        new TraceSpan { Name = "http.request", StartMs = 0, DurationMs = 120 },
        new TraceSpan { Name = "auth", StartMs = 2, DurationMs = 15, Depth = 1 },
    ];

    // Spans arriving from a live trace re-render the waterfall automatically.
    public void OnSpanReceived(string name, double start, double duration, int depth) =>
        Spans.Add(new TraceSpan { Name = name, StartMs = start, DurationMs = duration, Depth = depth });
}
<TraceChart SpansSource="{Binding Spans}" LabelWidth="18" />

A bound span list is flattened as-is; set each span's Depth to control indentation, or supply a nested tree via Children and let the chart derive it.

Interactivity

The waterfall is focusable and selectable. Give it focus (Tab, or click it) and use ↑/↓ — plus Home/End — to move the row selection, or click a row directly. The selected row is highlighted (brighter while the chart holds focus) and both SelectedIndex and SelectedSpan update two-way, so you can bind them to a details pane:

<TraceChart SpansSource="{Binding Spans}"
            SelectedSpan="{Binding Selected, Mode=TwoWay}" />

Key Concepts