Synchronized Chart in Blazor Charts Component
13 Sep 202421 minutes to read
A synchronized chart has the ability to access and interact with two or more charts at the same time. This means that actions performed on one chart, such as positioning a cursor, clicking on a specific point, or activating a tooltip, are reflected simultaneously on corresponding positions or data points across other synchronized charts based on their X-Y coordinates.
Tooltip synchronization
The tooltip can be synchronized across multiple charts using the ShowTooltip
and HideTooltip
methods. When we hover over a data point in one chart, we call the ShowTooltip
method for the other charts to display related information in other connected charts simultaneously.
In the ShowTooltip
method, specify the following parameters programmatically to enable tooltip for a particular chart:
- x - x-value of point or x-coordinate value.
- y - y-value of point or y-coordinate value.
@using Syncfusion.Blazor.Charts;
@using Syncfusion.Blazor
@using System.Net.Http.Json
@inject NavigationManager NavigationManager
@inject HttpClient Http
<div class="row">
<div class ="col">
<SfChart Title="USD to EUR" @ref="Chart1" Width="550">
<ChartEvents ChartMouseMove="OnMouseEventChart1" ChartMouseUp="OnMouseLeaveChart1" ChartMouseDown="ChartMouseDown"></ChartEvents>
<ChartArea>
<ChartTitleStyle TextAlignment="Alignment.Near"></ChartTitleStyle>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime" Minimum="new DateTime(2023, 02, 18)" Maximum="new DateTime(2023, 08, 18)" LabelFormat="MMM d" IntervalType="IntervalType.Months" LabelIntersectAction="LabelIntersectAction.Hide">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Minimum="0.86" Maximum="0.96" LabelFormat="n2" Interval="0.025">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@ChartPoints" XName="USD" YName="EUR" Type="ChartSeriesType.Line" Width="2" >
<ChartEmptyPointSettings Mode="EmptyPointMode.Drop"></ChartEmptyPointSettings>
</ChartSeries>
</ChartSeriesCollection>
<ChartTooltipSettings Enable="true" Shared="true" Header="" Format="<b>€${point.y}</b> <br> ${point.x} 2023" EnableMarker="false"></ChartTooltipSettings>
</SfChart>
</div>
<div class="col">
<SfChart Title="USD to JPY" @ref="Chart2" Width="550">
<ChartEvents ChartMouseMove="OnMouseEventChart2" ChartMouseUp="OnMouseLeaveChart2" ChartMouseDown="ChartMouseDown"></ChartEvents>
<ChartArea>
<ChartTitleStyle TextAlignment="Alignment.Near"></ChartTitleStyle>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime" Minimum="new DateTime(2023, 02, 18)" Maximum="new DateTime(2023, 08, 18)" LabelFormat="MMM d" IntervalType="IntervalType.Months" LabelIntersectAction="LabelIntersectAction.Hide">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Minimum="0.86" Maximum="0.96" LabelFormat="n2" Interval="0.025">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@ChartPoints" XName="USD" YName="EUR" Type="ChartSeriesType.Line" Width="2" >
<ChartEmptyPointSettings Mode="EmptyPointMode.Drop"></ChartEmptyPointSettings>
</ChartSeries>
</ChartSeriesCollection>
<ChartTooltipSettings Enable="true" Shared="true" Header="" Format="<b>€${point.y}</b> <br> ${point.x} 2023" EnableMarker="false"></ChartTooltipSettings>
</SfChart>
</div>
</div>
@code{
public SfChart? Chart1;
public SfChart? Chart2;
private bool isMouseMoveUpDetected;
private bool isTouch;
private bool isTapHold = false;
List<ToolbarItems> toolbarItems = new List<ToolbarItems>() { ToolbarItems.Pan, ToolbarItems.Reset };
protected override async Task OnInitializedAsync()
{
ChartPoints = await Http.GetFromJsonAsync<CurrencyRates[]>(NavigationManager.BaseUri + "./synchronized-data.json");
}
public class CurrencyRates
{
public DateTime USD { get; set; }
public double EUR { get; set; }
public double JPY { get; set; }
public double SGD { get; set; }
public double INR { get; set; }
}
public CurrencyRates[] ChartPoints { get; set; } = new CurrencyRates[] { };
public void ChartMouseDown(ChartMouseEventArgs changeEventArgs)
{
isMouseMoveUpDetected = false;
isTouch = true;
}
public void OnMouseEventChart1(ChartMouseEventArgs args)
{
Chart2.ShowTooltip(args.MouseX, args.MouseY, false);
}
public void OnMouseEventChart2(ChartMouseEventArgs args)
{
Chart1.ShowTooltip(args.MouseX, args.MouseY, false);
}
public void OnMouseLeaveChart1()
{
Chart2.HideTooltip();
}
public void OnMouseLeaveChart2()
{
Chart1.HideTooltip();
}
public void OnMouseLeaveChart1(ChartMouseEventArgs args)
{
isTapHold = false;
isTouch = false;
isMouseMoveUpDetected = true;
Chart2.HideTooltip();
}
public void OnMouseLeaveChart2(ChartMouseEventArgs args)
{
isTapHold = false;
isTouch = false;
isMouseMoveUpDetected = true;
Chart1.HideTooltip();
}
}
Crosshair synchronization
The crosshair can be synchronized across multiple charts using the ShowCrosshair
and HideCrosshair
methods. When we hover over one chart, we call the ShowCrosshair
method for the other charts to align with data points in other connected charts, simplifying data comparison and analysis.
In the ShowCrosshair
method, specify the following parameters programmatically to enable crosshair for a particular chart:
- x - x-coordinate value.
- y - y-coordinate value.
@using Syncfusion.Blazor.Charts;
@using Syncfusion.Blazor
@using System.Net.Http.Json
@inject NavigationManager NavigationManager
@inject HttpClient Http
<div class="row">
<div class="col">
<SfChart Title="USD to EUR" @ref="Chart1" Width="550">
<ChartEvents ChartMouseMove="OnMouseEventChart1" ChartMouseUp="OnMouseLeaveChart1" ChartMouseDown="ChartMouseDown" OnCrosshairMove="OnCrosshairMove" ></ChartEvents>
<ChartArea>
<ChartTitleStyle TextAlignment="Alignment.Near"></ChartTitleStyle>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime" Minimum="new DateTime(2023, 02, 18)" Maximum="new DateTime(2023, 08, 18)" LabelFormat="MMM d" IntervalType="IntervalType.Months" LabelIntersectAction="LabelIntersectAction.Hide">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Minimum="0.86" Maximum="0.96" LabelFormat="n2" Interval="0.025">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@ChartPoints" XName="USD" YName="EUR" Type="ChartSeriesType.Line" Width="2">
<ChartEmptyPointSettings Mode="EmptyPointMode.Drop"></ChartEmptyPointSettings>
</ChartSeries>
</ChartSeriesCollection>
<ChartCrosshairSettings Enable="true" DashArray="2,2" LineType="LineType.Vertical"></ChartCrosshairSettings>
</SfChart>
</div>
<div class="col">
<SfChart Title="USD to JPY" @ref="Chart2" Width="550">
<ChartEvents ChartMouseMove="OnMouseEventChart2" ChartMouseUp="OnMouseLeaveChart2" ChartMouseDown="ChartMouseDown" OnCrosshairMove="OnCrosshairMove" ></ChartEvents>
<ChartArea>
<ChartTitleStyle TextAlignment="Alignment.Near"></ChartTitleStyle>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime" Minimum="new DateTime(2023, 02, 18)" Maximum="new DateTime(2023, 08, 18)" LabelFormat="MMM d" IntervalType="IntervalType.Months" LabelIntersectAction="LabelIntersectAction.Hide">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Minimum="0.86" Maximum="0.96" LabelFormat="n2" Interval="0.025">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@ChartPoints" XName="USD" YName="EUR" Type="ChartSeriesType.Line" Width="2">
<ChartEmptyPointSettings Mode="EmptyPointMode.Drop"></ChartEmptyPointSettings>
</ChartSeries>
</ChartSeriesCollection>
<ChartCrosshairSettings Enable="true" DashArray="2,2" LineType="LineType.Vertical"></ChartCrosshairSettings>
</SfChart>
</div>
</div>
@code {
public SfChart? Chart1;
public SfChart? Chart2;
private bool isMouseMoveUpDetected;
private bool isTouch;
private bool isTapHold = false;
List<ToolbarItems> toolbarItems = new List<ToolbarItems>() { ToolbarItems.Pan, ToolbarItems.Reset };
protected override async Task OnInitializedAsync()
{
ChartPoints = await Http.GetFromJsonAsync<CurrencyRates[]>(NavigationManager.BaseUri + "./synchronized-data.json");
}
public class CurrencyRates
{
public DateTime USD { get; set; }
public double EUR { get; set; }
public double JPY { get; set; }
public double SGD { get; set; }
public double INR { get; set; }
}
public CurrencyRates[] ChartPoints { get; set; } = new CurrencyRates[] { };
public void OnCrosshairMove(CrosshairMoveEventArgs changeEventArgs)
{
isTapHold = isMouseMoveUpDetected ? false : true;
}
public void ChartMouseDown(ChartMouseEventArgs changeEventArgs)
{
isMouseMoveUpDetected = false;
isTouch = true;
}
public void OnMouseEventChart1(ChartMouseEventArgs args)
{
Chart2.ShowCrosshair(args.MouseX, args.MouseY);
}
public void OnMouseEventChart2(ChartMouseEventArgs args)
{
Chart1.ShowCrosshair(args.MouseX, args.MouseY);
}
public void OnMouseLeaveChart1()
{
Chart2.HideCrosshair();
}
public void OnMouseLeaveChart2()
{
Chart1.HideCrosshair();
}
public void OnMouseLeaveChart1(ChartMouseEventArgs args)
{
isTapHold = false;
isTouch = false;
isMouseMoveUpDetected = true;
Chart2.HideCrosshair();
}
public void OnMouseLeaveChart2(ChartMouseEventArgs args)
{
isTapHold = false;
isTouch = false;
isMouseMoveUpDetected = true;
Chart1.HideCrosshair();
}
}
Zooming synchronization
You can maintain constant zoom levels across multiple charts using the OnZoomEnd event. In the OnZoomEnd event, obtain the ZoomFactor
and ZoomPosition
values of the particular chart, and then apply those values to the other charts.
@using Syncfusion.Blazor.Charts;
@using Syncfusion.Blazor
@using System.Net.Http.Json
@inject NavigationManager NavigationManager
@inject HttpClient Http
<div class="row">
<div class="col">
<SfChart Title="USD to EUR" @ref="_chart1" Width="550">
<ChartEvents OnZoomEnd="ZoomEvent" OnZooming="ZoomEvent"></ChartEvents>
<ChartArea>
<ChartTitleStyle TextAlignment="Alignment.Near"></ChartTitleStyle>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ZoomFactor=@zoomFactor ZoomPosition=@zoomPosition ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime" Minimum="new DateTime(2023, 02, 18)" Maximum="new DateTime(2023, 08, 18)" LabelFormat="MMM d" IntervalType="IntervalType.Months" LabelIntersectAction="LabelIntersectAction.Hide">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Minimum="0.86" Maximum="0.96" LabelFormat="n2" Interval="0.025">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@ChartPoints" XName="USD" YName="EUR" Type="ChartSeriesType.Line" Width="2">
<ChartEmptyPointSettings Mode="EmptyPointMode.Drop"></ChartEmptyPointSettings>
</ChartSeries>
</ChartSeriesCollection>
<ChartZoomSettings EnableMouseWheelZooming="true" EnablePinchZooming="true" EnableScrollbar="false" EnableDeferredZooming="false" EnablePan="true" Mode="ZoomMode.X" ToolbarItems="@toolbarItems"></ChartZoomSettings>
</SfChart>
</div>
<div class="col">
<SfChart Title="USD to JPY" @ref="_chart2" Width="550">
<ChartEvents OnZoomEnd="ZoomEvent" OnZooming="ZoomEvent"></ChartEvents>
<ChartArea>
<ChartTitleStyle TextAlignment="Alignment.Near"></ChartTitleStyle>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ZoomFactor=@zoomFactor ZoomPosition=@zoomPosition ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime" Minimum="new DateTime(2023, 02, 18)" Maximum="new DateTime(2023, 08, 18)" LabelFormat="MMM d" IntervalType="IntervalType.Months" LabelIntersectAction="LabelIntersectAction.Hide">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Minimum="0.86" Maximum="0.96" LabelFormat="n2" Interval="0.025">
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@ChartPoints" XName="USD" YName="EUR" Type="ChartSeriesType.Line" Width="2">
<ChartEmptyPointSettings Mode="EmptyPointMode.Drop"></ChartEmptyPointSettings>
</ChartSeries>
</ChartSeriesCollection>
<ChartZoomSettings EnableMouseWheelZooming="true" EnablePinchZooming="true" EnableScrollbar="false" EnableDeferredZooming="false" EnablePan="true" Mode="ZoomMode.X" ToolbarItems="@toolbarItems"></ChartZoomSettings>
</SfChart>
</div>
</div>
@code {
public SfChart? _chart1;
public SfChart? _chart2;
public double zoomFactor = 1;
public double zoomPosition = 0;
private bool isZooming;
List<ToolbarItems> toolbarItems = new List<ToolbarItems>() { ToolbarItems.Pan, ToolbarItems.Reset };
protected override async Task OnInitializedAsync()
{
ChartPoints = await Http.GetFromJsonAsync<CurrencyRates[]>(NavigationManager.BaseUri + "./synchronized-data.json");
}
public class CurrencyRates
{
public DateTime USD { get; set; }
public double EUR { get; set; }
public double JPY { get; set; }
public double SGD { get; set; }
public double INR { get; set; }
}
public CurrencyRates[] ChartPoints { get; set; } = new CurrencyRates[] { };
public void ZoomEvent(ZoomingEventArgs args)
{
isZooming = args.Name == "OnZooming";
if (args.AxisCollection != null && args.AxisCollection.Count > 0)
{
zoomFactor = args.AxisCollection.Find(item => item.AxisName == "PrimaryXAxis").ZoomFactor;
zoomPosition = args.AxisCollection.Find(item => item.AxisName == "PrimaryXAxis").ZoomPosition;
InvokeAsync(StateHasChanged);
}
}
}
Check out the JSON file used for the above samples from here Synchronized Chart Data.
NOTE
Refer to our Blazor Charts feature tour page for its groundbreaking feature representations and also explore our Blazor Chart Example to know various chart types and how to represent time-dependent data, showing trends at equal intervals.