Working with Data in Blazor Stock Chart Component
17 Nov 202323 minutes to read
The Stock Chart uses SfDataManager, which supports both RESTful JSON data services binding and IEnumerable binding. The DataSource value can be set using either SfDataManager property value or a list of business objects.
It supports the following data binding methods:
- List binding
- Remote data
List binding
To do list binding to the stock chart, an IEnumerable object can be assigned to the DataSource property. The list data source can also be provided as an instance of the SfDataManager or by using SfDataManager component. Now map the fields in list to
XName, High, Low, Open and Close
properties.
@using Syncfusion.Blazor.Charts
<SfStockChart Title="AAPL Stock Price">
<StockChartSeriesCollection>
<StockChartSeries DataSource="@StockDetails" Type="ChartSeriesType.Candle" XName="Date" High="High" Low="Low" Open="Open" Close="Close" Volume="Volume" Name="Google"></StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code {
public class ChartData
{
public DateTime Date {get; set;}
public Double Open {get; set;}
public Double Low {get; set;}
public Double Close {get; set;}
public Double High {get; set;}
public Double Volume{get; set;}
}
public List<ChartData> StockDetails = new List<ChartData>
{
new ChartData { Date = new DateTime(2012, 04, 02), Open= 85.9757, High = 90.6657,Low = 85.7685, Close = 90.5257,Volume = 660187068},
new ChartData { Date = new DateTime(2012, 04, 09), Open= 89.4471, High = 92,Low = 86.2157, Close = 86.4614,Volume = 912634864},
new ChartData { Date = new DateTime(2012, 04, 16), Open= 87.1514, High = 88.6071,Low = 81.4885, Close = 81.8543,Volume = 1221746066},
new ChartData { Date = new DateTime(2012, 04, 23), Open= 81.5157, High = 88.2857,Low = 79.2857, Close = 86.1428,Volume = 965935749},
new ChartData { Date = new DateTime(2012, 04, 30), Open= 85.4, High = 85.4857,Low = 80.7385, Close = 80.75,Volume = 615249365},
new ChartData { Date = new DateTime(2012, 05, 07), Open= 80.2143, High = 82.2685,Low = 79.8185, Close = 80.9585,Volume = 541742692},
new ChartData { Date = new DateTime(2012, 05, 14), Open= 80.3671, High = 81.0728,Low = 74.5971, Close = 75.7685,Volume = 708126233},
new ChartData { Date = new DateTime(2012, 05, 21), Open= 76.3571, High = 82.3571,Low = 76.2928, Close = 80.3271,Volume = 682076215},
new ChartData { Date = new DateTime(2012, 05, 28), Open= 81.5571, High = 83.0714,Low = 80.0743, Close = 80.1414,Volume = 480059584}
};
}
NOTE
By default, SfDataManager uses BlazorAdaptor for list data-binding.
ExpandoObject binding
Stock Chart is a generic component which is strongly bound to a model type. There are cases when the model type is unknown during compile time. In such circumstances data can be bound to the Stock chart as a list of ExpandoObject. The ExpandoObject can be bound to Stock chart by assigning to the DataSource property.
@using Syncfusion.Blazor.Charts
@using System.Dynamic
<SfStockChart Title="AAPL Stock Price">
<StockChartSeriesCollection>
<StockChartSeries DataSource="@StockInfo" Type="ChartSeriesType.Candle" XName="X" High="High" Low="Low" Open="Open" Close="Close" Volume="Volume" Name="Google"></StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code{
private List<DateTime> Dates = new List<DateTime> { new DateTime(2005, 01, 01), new DateTime(2006, 01, 01),
new DateTime(2007, 01, 01), new DateTime(2008, 01, 01), new DateTime(2009, 01, 01), new DateTime(2010, 01, 01), new DateTime(2011, 01, 01) };
public DateTime[] Value = new DateTime[] { new DateTime(2006, 01, 01), new DateTime(2008, 01, 01) };
public List<ExpandoObject> StockInfo { get; set; } = new List<ExpandoObject>();
private Random randomNum = new Random();
protected override void OnInitialized()
{
StockInfo = Enumerable.Range(0, 6).Select((x) =>
{
dynamic d = new ExpandoObject();
d.X = Dates[x];
d.Open = randomNum.Next(75, 85);
d.High = randomNum.Next(88, 92);
d.Low = randomNum.Next(76, 86);
d.Close = randomNum.Next(85, 90);
d.Volume = randomNum.Next(660187068, 965935749);
return d;
}).Cast<ExpandoObject>().ToList<ExpandoObject>();
}
}
DynamicObject binding
Stock Chart supports DynamicObject data source when the model type is unknown. The DynamicObject can be bound to Stock chart by assigning to the DataSource property.
@using Syncfusion.Blazor.Charts
@using System.Dynamic
<SfStockChart Title="AAPL Stock Price">
<StockChartSeriesCollection>
<StockChartSeries DataSource="@MedalDetails" Type="ChartSeriesType.Candle" XName="X" High="High" Low="Low" Open="Open" Close="Close" Volume="Volume" Name="Google"></StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code{
private List<DateTime> Dates = new List<DateTime> { new DateTime(2005, 01, 01), new DateTime(2006, 01, 01),
new DateTime(2007, 01, 01), new DateTime(2008, 01, 01), new DateTime(2009, 01, 01), new DateTime(2010, 01, 01), new DateTime(2011, 01, 01) };
public DateTime[] Value = new DateTime[] { new DateTime(2006, 01, 01), new DateTime(2008, 01, 01) };
private Random randomNum = new Random();
public List<DynamicDictionary> MedalDetails = new List<DynamicDictionary>() { };
protected override void OnInitialized()
{
MedalDetails = Enumerable.Range(0, 5).Select((x) =>
{
dynamic d = new DynamicDictionary();
d.X = Dates[x];
d.Open = randomNum.Next(75, 85);
d.High = randomNum.Next(88, 92);
d.Low = randomNum.Next(76, 86);
d.Close = randomNum.Next(85, 90);
d.Volume = randomNum.Next(660187068, 965935749);
return d;
}).Cast<DynamicDictionary>().ToList<DynamicDictionary>();
}
public class DynamicDictionary : DynamicObject
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
return dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames()
{
return this.dictionary?.Keys;
}
}
}
Remote Data
Binding with OData services
OData is a standardized protocol for creating and consuming data. You can retrieve data from OData service using the SfDataManager. Refer to the following code example for remote data binding using OData service.
@using Syncfusion.Blazor.Charts
@using Syncfusion.Blazor.Data
<SfStockChart Title="Remote Data" EnableSelector="false" TrendlineType="@TrendlineType" IndicatorType="@Indicator" SeriesType="@SeriesType" ExportType="@ExportType">
<SfDataManager Url="https://mvc.syncfusion.com/Services/Northwnd.svc/Tasks/" CrossDomain="true" Adaptor="Adaptors.ODataAdaptor"></SfDataManager>
<StockChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Category"></StockChartPrimaryXAxis>
<StockChartPrimaryYAxis Maximum="3"></StockChartPrimaryYAxis>
<StockChartSeriesCollection>
<StockChartSeries XName="Assignee" YName="Estimate" Query="new ej.data.Query().take(10).where('Estimate', 'lessThan', 3, false)" Type="ChartSeriesType.Column">
</StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code {
public List<TrendlineTypes> TrendlineType = new List<TrendlineTypes>();
public List<TechnicalIndicators> Indicator = new List<TechnicalIndicators>();
public List<ChartSeriesType> SeriesType = new List<ChartSeriesType>();
public List<ExportType> ExportType = new List<ExportType>();
}
Binding with OData v4 services
The SfDataManager can retrieve and consume OData v4 services, which is an upgraded version of OData protocols. Refer to the OData documentation for additional information on OData v4 services. To bind an OData v4 service, use the ODataV4Adaptor.
@using Syncfusion.Blazor.Charts
@using Syncfusion.Blazor.Data
<SfStockChart Title="Remote Data" EnableSelector="false" TrendlineType="@TrendlineType" IndicatorType="@Indicator" SeriesType="@SeriesType" ExportType="@ExportType">
<SfDataManager Url="https://mvc.syncfusion.com/Services/Northwnd.svc/Tasks/" CrossDomain="true" Adaptor="Adaptors.ODataV4Adaptor"></SfDataManager>
<StockChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Category"></StockChartPrimaryXAxis>
<StockChartPrimaryYAxis Maximum="3"></StockChartPrimaryYAxis>
<StockChartSeriesCollection>
<StockChartSeries XName="Assignee" YName="Estimate" Query="new ej.data.Query().take(10).where('Estimate', 'lessThan', 3, false)" Type="ChartSeriesType.Column">
</StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code {
public List<TrendlineTypes> TrendlineType = new List<TrendlineTypes>();
public List<TechnicalIndicators> Indicator = new List<TechnicalIndicators>();
public List<ChartSeriesType> SeriesType = new List<ChartSeriesType>();
public List<ExportType> ExportType = new List<ExportType>();
}
Web API
The WebApiAdaptor can be used to bind a Stock chart to a Web API created using an OData endpoint.
@using Syncfusion.Blazor.Charts
@using Syncfusion.Blazor.Data
<SfStockChart Title="Remote Data" EnableSelector="false" TrendlineType="@TrendlineType" IndicatorType="@Indicator" SeriesType="@SeriesType" ExportType="@ExportType">
<SfDataManager Url="https://mvc.syncfusion.com/Services/Northwnd.svc/Tasks/" CrossDomain="true" Adaptor="Adaptors.WebAPIAdaptor"></SfDataManager>
<StockChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Category"></StockChartPrimaryXAxis>
<StockChartPrimaryYAxis Maximum="3"></StockChartPrimaryYAxis>
<StockChartSeriesCollection>
<StockChartSeries XName="Assignee" YName="Estimate" Query="new ej.data.Query().take(10).where('Estimate', 'lessThan', 3, false)" Type="ChartSeriesType.Column">
</StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code {
public List<TrendlineTypes> TrendlineType = new List<TrendlineTypes>();
public List<TechnicalIndicators> Indicator = new List<TechnicalIndicators>();
public List<ChartSeriesType> SeriesType = new List<ChartSeriesType>();
public List<ExportType> ExportType = new List<ExportType>();
}
Observable collection
The ObservableCollection (dynamic data collection) provides notifications when items are added, removed, and moved. The implemented INotifyCollectionChanged provides notification when the dynamic changes of adding, removing, moving, and clearing the collection occur.
@using Syncfusion.Blazor.Charts
@using System.Collections.ObjectModel;
<SfStockChart Title="AAPL Stock Price">
<StockChartSeriesCollection>
<StockChartSeries DataSource="@ChartPoints" Type="ChartSeriesType.Candle" XName="Date" High="High" Low="Low" Open="Open" Close="Close" Volume="Volume" Name="Google"></StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code {
public ObservableCollection<ChartData> ChartPoints { get; set; }
public DateTime[] Value = new DateTime[] { new DateTime(2006, 01, 01), new DateTime(2008, 01, 01) };
public class ChartData
{
public DateTime Date { get; set; }
public double High { get; set; }
public double Low { get; set; }
public double Volume { get; set; }
public double Close { get; set; }
public double Open { get; set; }
public static ObservableCollection<ChartData> GetData()
{
ObservableCollection<ChartData> ChartPoints = new ObservableCollection<ChartData>()
{
new ChartData { Date = new DateTime(2012, 04, 02), Open= 85.9757, High = 90.6657,Low = 85.7685, Close = 90.5257,Volume = 660187068},
new ChartData { Date = new DateTime(2012, 04, 09), Open= 89.4471, High = 92,Low = 86.2157, Close = 86.4614,Volume = 912634864},
new ChartData { Date = new DateTime(2012, 04, 16), Open= 87.1514, High = 88.6071,Low = 81.4885, Close = 81.8543,Volume = 1221746066},
new ChartData { Date = new DateTime(2012, 04, 23), Open= 81.5157, High = 88.2857,Low = 79.2857, Close = 86.1428,Volume = 965935749},
new ChartData { Date = new DateTime(2012, 04, 30), Open= 85.4, High = 85.4857,Low = 80.7385, Close = 80.75,Volume = 615249365},
new ChartData { Date = new DateTime(2012, 05, 07), Open= 80.2143, High = 82.2685,Low = 79.8185, Close = 80.9585,Volume = 541742692},
new ChartData { Date = new DateTime(2012, 05, 14), Open= 80.3671, High = 81.0728,Low = 74.5971, Close = 75.7685,Volume = 708126233},
new ChartData { Date = new DateTime(2012, 05, 21), Open= 76.3571, High = 82.3571,Low = 76.2928, Close = 80.3271,Volume = 682076215},
new ChartData { Date = new DateTime(2012, 05, 28), Open= 81.5571, High = 83.0714,Low = 80.0743, Close = 80.1414,Volume = 480059584}
};
return ChartPoints;
}
}
protected override void OnInitialized()
{
this.ChartPoints = ChartData.GetData();
}
}
Entity Framework
Entity Framework acts as a modern object-database mapper for .NET. This section explains how to consume data from the Microsoft SQL Server database and bind it to the chart component.
Create DBContext class
The first step is to create a DBContext class called OrderContext for establishing connection to a Microsoft SQL Server database.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using EFChart.Data;
namespace EFChart.Data
{
public class OrderContext : DbContext
{
public virtual DbSet<Order> Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// Configures the context to connect to a Microsoft SQL Serve database
optionsBuilder.UseSqlServer(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename='D:\blazor\EFTreeMap\App_Data\NORTHWND.MDF';Integrated Security=True;Connect Timeout=30");
}
}
}
public class Order
{
[Key]
public int? OrderID { get; set; }
[Required]
public string CustomerID { get; set; }
[Required]
public int EmployeeID { get; set; }
}
}
Create data access layer to perform data operation
Now, create a class called OrderDataAccessLayer, which acts as a data access layer to retrieve the records from the database table.
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static BlazorApp1.Data.OrderContext;
using EFChart.Data;
namespace EFChart.Data
{
public class OrderDataAccessLayer
{
OrderContext db = new OrderContext();
//To Get all Orders details
public DbSet<Order> GetAllOrders()
{
try
{
return db.Orders;
}
catch
{
throw;
}
}
}
}
Creating Web API Controller
A Web API Controller must be created which allows the chart to directly consume data from the Entity Framework.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using static BlazorApp1.Data.OrderContext;
using EFChart.Data;
namespace EFChart.Controller
{
[Route("api/[controller]")]
[ApiController]
public class DefaultController : ControllerBase
{
OrderDataAccessLayer db = new OrderDataAccessLayer();
[HttpGet]
public object Get()
{
IQueryable<Order> data = db.GetAllOrders().AsQueryable();
var count = data.Count();
var queryString = Request.Query;
if (queryString.Keys.Contains("$inlinecount"))
{
StringValues Skip;
StringValues Take;
int skip = (queryString.TryGetValue("$skip", out Skip)) ? Convert.ToInt32(Skip[0]) : 0;
int top = (queryString.TryGetValue("$top", out Take)) ? Convert.ToInt32(Take[0]) : data.Count();
return new { Items = data.Skip(skip).Take(top), Count = count };
}
else
{
return data;
}
}
}
}
Add Web API Controller services in Startup.cs
Open the Startup.cs file and add services and endpoints required for Web API Controller as follows.
using EFChart.Data;
using Newtonsoft.Json.Serialization;
namespace BlazorApplication
{
public class Startup
{
....
....
public void ConfigureServices(IServiceCollection services)
{
....
....
services.AddSingleton<OrderDataAccessLayer>();
// Adds services for controllers to the specified Microsoft.Extensions.DependencyInjection.IServiceCollection.
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
....
....
app.UseEndpoints(endpoints =>
{
// Adds endpoints for controller actions to the Microsoft.AspNetCore.Routing.IEndpointRouteBuilder
endpoints.MapDefaultControllerRoute();
.....
.....
});
}
}
}
Configure chart component
Configure the chart to bind data using either DataSource property or SfDataManager.
For instance, to bind data directly from the data access layer class OrderDataAccessLayer, assign the DataSource property to be OrderData.GetAllOrders().
@using EFChart.Data;
@inject OrderDataAccessLayer OrderData;
@using Syncfusion.Blazor.Charts
<SfStockChart EnableSelector="false" DataSource="@OrderData.GetAllOrders()">
<StockChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Category"></StockChartPrimaryXAxis>
<StockChartSeriesCollection>
<StockChartSeries Type="ChartSeriesType.Column" XName="CustomerID" YName="OrderID"></StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
@code{
}
On the other hand, to configure the chart using Web API, provide the appropriate endpoint Url within SfDataManager along with Adaptor. Here, use WebApiAdaptor in-order to interact with the Web API to consume data from the Entity Framework appropriately.
@using Syncfusion.Blazor.Charts
@using Syncfusion.Blazor.Data
<div class="control-section">
<div>
<SfStockChart EnableSelector="false">
<SfDataManager Url="api/Default" Adaptor="Syncfusion.Blazor.Adaptors.WebApiAdaptor">
</SfDataManager>
<StockChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.Category"></StockChartPrimaryXAxis>
<StockChartSeriesCollection>
<StockChartSeries Type="ChartSeriesType.Column" XName="CustomerID" YName="OrderID"></StockChartSeries>
</StockChartSeriesCollection>
</SfStockChart>
</div>
</div>
@code{
}