Blazorise SVG Chart Streaming
Append live data and keep the chart viewport moving.
SVG chart streaming is configured with SvgChartStreamingOptions. Set Duration to a time window for rolling retention, or leave Duration as null for infinite retention.
Examples
Append values
Append values through the chart reference. A timer can call the same method in a live application.Append a value or hover over a point.
@using System.Threading @implements IAsyncDisposable <Button Color="Color.Primary" Outline Clicked=""> Append value </Button> <SvgLineChart @ref="chart" TItem="object" Data="" Options="" Streaming="" Hovered=""> <SvgChartTitle Title='@("Live latency")' Subtitle='@("Right-to-left viewport with infinite retention")' /> <SvgChartTooltip Enabled /> <SvgChartValueAxis BeginAtZero TickCount="6" /> </SvgLineChart> <Paragraph Margin="Margin.Is2.FromTop.Is0.FromBottom">@lastEvent</Paragraph>
@code { private static readonly TimeSpan StreamingInterval = TimeSpan.FromSeconds( 1 ); private SvgLineChart<object> chart; private readonly Random random = new(); private double currentLatency = 45; private string lastEvent = "Append a value or hover over a point."; private CancellationTokenSource streamingCancellationTokenSource; private Task streamingTask; private readonly SvgChartOptions options = new() { Height = 360, Legend = new() { Visible = false }, XAxis = new() { GridLines = new() { Visible = true }, Labels = new() { Step = 2, Offset = 30, }, }, }; private readonly SvgChartStreamingOptions streaming = new() { Enabled = true, VisibleDataPoints = 12, Duration = null, Reverse = false, Animation = new() { Duration = StreamingInterval, }, RefreshInterval = TimeSpan.FromMilliseconds( 500 ), }; private readonly SvgChartData<double?> data = new() { Series = [ new() { Name = "Latency", Color = Color.Primary, }, ], }; protected override Task OnAfterRenderAsync( bool firstRender ) { if ( firstRender ) { streamingCancellationTokenSource = new(); streamingTask = RunStreamingAsync( streamingCancellationTokenSource.Token ); } return Task.CompletedTask; } private async Task RunStreamingAsync( CancellationToken cancellationToken ) { try { using PeriodicTimer timer = new( StreamingInterval ); while ( await timer.WaitForNextTickAsync( cancellationToken ) ) { await InvokeAsync( AppendValue ); } } catch ( OperationCanceledException ) { } } private async Task AppendValue() { string label = DateTime.Now.ToString( "HH:mm:ss" ); currentLatency = Math.Clamp( currentLatency + random.Next( -16, 17 ), 25, 80 ); if ( chart is not null ) await chart.AppendValue( "Latency", label, currentLatency ); } private Task OnPointHovered( SvgChartPointEventArgs eventArgs ) { lastEvent = $"Hovered {eventArgs.SeriesName} / {eventArgs.Category}: {eventArgs.Value}"; return Task.CompletedTask; } public async ValueTask DisposeAsync() { if ( streamingCancellationTokenSource is not null ) { await streamingCancellationTokenSource.CancelAsync(); streamingCancellationTokenSource.Dispose(); } if ( streamingTask is not null ) await streamingTask; } }
Rolling window
UseDuration to retain only recent timestamped samples.
@using System.Threading @implements IAsyncDisposable <Button Color="Color.Primary" Outline Clicked=""> Append sample </Button> <SvgAreaChart @ref="chart" TItem="object" Data="" Options=""> <SvgChartTitle Title='@("Rolling throughput")' Subtitle='@("Retains the last 20 seconds of samples")' /> <SvgChartTooltip Enabled /> <SvgChartStreaming Enabled VisibleDataPoints="10" Duration="@TimeSpan.FromSeconds( 20 )" Animation="" /> <SvgChartTimeAxis Format="HH:mm:ss" /> <SvgChartValueAxis BeginAtZero TickCount="6" /> </SvgAreaChart>
@code { private static readonly TimeSpan StreamingInterval = TimeSpan.FromSeconds( 1 ); private readonly Random random = new( 24 ); private SvgAreaChart<object> chart; private double currentThroughput = 64; private CancellationTokenSource streamingCancellationTokenSource; private Task streamingTask; private readonly SvgChartOptions options = new() { Height = 360, Legend = new() { Visible = false }, XAxis = new() { GridLines = new() { Visible = true }, Labels = new() { Step = 2, Offset = 30 }, }, }; private readonly SvgChartStreamingAnimationOptions streamingAnimation = new() { Duration = StreamingInterval, }; private readonly SvgChartData<double?> data = new() { Series = [ new() { Name = "Throughput", Color = Color.Success, }, ], }; protected override Task OnAfterRenderAsync( bool firstRender ) { if ( firstRender ) { streamingCancellationTokenSource = new(); streamingTask = RunStreamingAsync( streamingCancellationTokenSource.Token ); } return Task.CompletedTask; } private async Task RunStreamingAsync( CancellationToken cancellationToken ) { try { using PeriodicTimer timer = new( StreamingInterval ); while ( await timer.WaitForNextTickAsync( cancellationToken ) ) { await InvokeAsync( AppendValue ); } } catch ( OperationCanceledException ) { } } private async Task AppendValue() { currentThroughput = Math.Clamp( currentThroughput + random.Next( -10, 11 ), 35, 95 ); if ( chart is not null ) await chart.AppendValue( "Throughput", DateTimeOffset.Now, currentThroughput ); } public async ValueTask DisposeAsync() { if ( streamingCancellationTokenSource is not null ) { await streamingCancellationTokenSource.CancelAsync(); streamingCancellationTokenSource.Dispose(); } if ( streamingTask is not null ) await streamingTask; } }
API
See the API reference for the parameters, events, methods, and related types available to the components covered on this page.