blazor-405316-components-charts-user-interaction-options.md
DevExpress Blazor Charts allow users to interact with their content. This article describes available options.
Blazor Charts allow users to select series (SeriesSelectionMode) and points (PointSelectionMode). You can choose between single and multiple selection, or disable this functionality. For line- and area-based series, the PointSelectionMode property has priority over the SeriesSelectionMode property. You cannot use both these properties simultaneously.
The following example enables multiple point selection:
<DxChart Data="DataSource" PointSelectionMode="ChartSelectionMode.Multiple">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)" />
<DxChartLegend Visible="false" />
</DxChart>
<DxPieChart Data="DataSource" PointSelectionMode="ChartSelectionMode.Multiple">
<DxPieChartSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)" />
<DxChartLegend Visible="false" />
</DxPieChart>
<DxPolarChart Data="DataSource" PointSelectionMode="ChartSelectionMode.Multiple">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)" />
<DxChartLegend Visible="false" />
</DxPolarChart>
public class DataPoint {
public string Country { get; set; }
public double AppleProduction { get; set; }
public double GrapeProduction { get; set; }
public DataPoint(string country, double appleProduction, double grapeProduction) {
Country = country;
AppleProduction = appleProduction;
GrapeProduction = grapeProduction;
}
}
public class DataPointProvider {
public static List<DataPoint> ReturnPoints() {
return new List<DataPoint>() {
new DataPoint("USA", 4.21, 6.22),
new DataPoint("China", 3.33, 8.65),
new DataPoint("Turkey", 2.6, 4.25),
new DataPoint("Italy", 2.2, 7.78),
new DataPoint("India", 2.16, 2.26),
};
}
}
When you enable point selection, a click can select a single or multiple points. You can specify a SelectionMode on the series level for line- and area-based series, and on the point level for other series types.
Note
The SelectionMode property does not apply to pie series.
The following example sets SelectionMode to AllPoints. The chart selects all point in a series on a single click.
<DxChart Data="DataSource"
PointSelectionMode="ChartSelectionMode.Multiple">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)">
<DxChartSeriesPoint SelectionMode="ChartSeriesPointSelectionMode.AllPoints" />
</DxChartLineSeries>
<DxChartLegend Visible="false" />
</DxChart>
<DxPolarChart Data="DataSource"
SeriesSelectionMode="ChartSelectionMode.Multiple">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)">
<DxChartSeriesPoint SelectionMode="ChartSeriesPointSelectionMode.AllPoints" />
</DxPolarChartLineSeries>
<DxChartLegend Visible="false" />
</DxPolarChart>
When you enable series selection, Charts highlight the entire series. For line- and area-based series, use their SelectionMode properties to specify highlighted series elements.
The following example allows users to select the Apples series and disables selection for the Grapes series:
<DxChart Data="DataSource"
SeriesSelectionMode="ChartSelectionMode.Single">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
Name="Apples" />
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.GrapeProduction)"
SelectionMode="ChartContinuousSeriesSelectionMode.None"
Name="Grapes" />
<DxChartLegend Position="RelativePosition.Outside" />
</DxChart>
<DxPolarChart Data="DataSource"
SeriesSelectionMode="ChartSelectionMode.Single">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
Name="Apples" />
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.GrapeProduction)"
SelectionMode="ChartContinuousSeriesSelectionMode.None"
Name="Grapes" />
<DxChartLegend Position="RelativePosition.Outside" />
</DxPolarChart>
Blazor Charts support point and series selection in code. Call the following methods and their overloads:
SelectPoints | DeselectPointsSelect and deselect specific series points.SelectSeries | DeselectSeriesSelect and deselect a specific series.ClearSelectionAsync()Resets selection in the chart.
Handle the SelectionChanged event to respond to selection changes.
The following example selects a series on a button click:
<DxButton Text="Select Series"
Click="OnClick" />
<DxChart Data="DataSource"
@ref="Chart"
SeriesSelectionMode="ChartSelectionMode.Single">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
Name="Production" />
<DxChartLegend Visible="false" />
</DxChart>
@code {
DxChart<DataPoint> Chart { get; set; }
void OnClick() {
Chart.SelectSeries("Production");
}
}
<DxButton Text="Select Series"
Click="OnClick" />
<DxPieChart Data="DataSource"
@ref="Chart"
SeriesSelectionMode="ChartSelectionMode.Single">
<DxPieChartSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
Name="Production" />
<DxChartLegend Visible="false" />
</DxPieChart>
@code {
DxPieChart<DataPoint> Chart { get; set; }
void OnClick() {
Chart.SelectSeries("Production");
}
}
<DxButton Text="Select Series"
Click="OnClick" />
<DxPolarChart Data="DataSource"
@ref="Chart"
SeriesSelectionMode="ChartSelectionMode.Single">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
Name="Production" />
<DxChartLegend Visible="false" />
</DxChart>
@code {
DxPolarChart<DataPoint> Chart { get; set; }
void OnClick() {
Chart.SelectSeries("Production");
}
}
Charts highlight entire pie and line- and area-based series when a user hovers over them. Use the series HoverMode property to specify highlighted series elements.
The following code sets hover highlight mode to a single series point:
<DxChart Data="DataSource">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
HoverMode="ChartContinuousSeriesHoverMode.NearestPoint" />
<DxChartLegend Visible="false" />
</DxChart>
<DxPieChart Data="DataSource">
<DxPieChartSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
HoverMode="PieChartSeriesHoverMode.Point" />
<DxChartLegend Visible="false" />
</DxPieChart>
<DxPolarChart Data="DataSource">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)"
HoverMode="ChartContinuousSeriesHoverMode.NearestPoint" />
<DxChartLegend Visible="false" />
</DxPolarChart>
public class DataPoint {
public string Country { get; set; }
public double AppleProduction { get; set; }
public double GrapeProduction { get; set; }
public DataPoint(string country, double appleProduction, double grapeProduction) {
Country = country;
AppleProduction = appleProduction;
GrapeProduction = grapeProduction;
}
}
public class DataPointProvider {
public static List<DataPoint> ReturnPoints() {
return new List<DataPoint>() {
new DataPoint("USA", 4.21, 6.22),
new DataPoint("China", 3.33, 8.65),
new DataPoint("Turkey", 2.6, 4.25),
new DataPoint("Italy", 2.2, 7.78),
new DataPoint("India", 2.16, 2.26),
};
}
}
Use the HoverMode property to specify series points to highlight on hover. This property exists on the point level for line- and area-based series, and on the series level for other series types. Refer to the following enumeration description for additional information about available options: ChartSeriesPointHoverMode. Point behavior is the same as the series-level setting if HoverMode is None.
The following example changes hover mode. The chart highlights all series points when a user hovers the mouse pointer over a line series:
<DxChart Data="DataSource">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)">
<DxChartSeriesPoint HoverMode="ChartSeriesPointHoverMode.AllPoints" />
</DxChartLineSeries>
<DxChartLegend Visible="false" />
</DxChart>
<DxPolarChart Data="DataSource">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)">
<DxChartSeriesPoint HoverMode="ChartSeriesPointHoverMode.AllPoints" />
</DxPolarChartLineSeries>
<DxChartLegend Visible="false" />
</DxPolarChart>
If you want to execute custom logic when users interact with series, handle the SeriesClick event. The following example displays information about the clicked point:
<DxChart Data="DataSource" SeriesClick="Chart_SeriesClick">
<DxChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)" />
<DxChartLegend Visible="false" />
</DxChart>
@Argument
@Value
@code {
string Argument { get; set; }
double Value { get; set; }
void Chart_SeriesClick(ChartSeriesClickEventArgs args) {
if (args.Point != null) {
Argument = args.Point.Argument.ToString();
Value = (double)args.Point.Value;
}
}
}
<DxPieChart Data="DataSource" SeriesClick="Pie_SeriesClick">
<DxPieChartSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)" />
<DxChartLegend Visible="false" />
</DxPieChart>
@Argument
@Value
@code {
string Argument { get; set; }
double Value { get; set; }
void Pie_SeriesClick(PieChartSeriesClickEventArgs args) {
if (args.Point != null) {
Argument = args.Point.Argument.ToString();
Value = (double)args.Point.Value;
}
}
}
<DxPolarChart Data="DataSource" SeriesClick="Polar_SeriesClick">
<DxPolarChartLineSeries ArgumentField="@((DataPoint v) => v.Country)"
ValueField="@((DataPoint v) => v.AppleProduction)" />
<DxChartLegend Visible="false" />
</DxPolarChart>
@Argument
@Value
@code {
string Argument { get; set; }
double Value { get; set; }
void Polar_SeriesClick(PolarChartSeriesClickEventArgs args) {
if (args.Point != null) {
Argument = args.Point.Argument.ToString();
Value = (double)args.Point.Value;
}
}
}
This section contains code samples that demonstrate user interaction options.
The following example uses a SeriesTemplate to create series. SeriesClick and onclick event handlers implement navigation between data detail levels. Depending on the level, the chart loads and displays corresponding data. Data fields that correspond to arguments and series names are used as expressions to obtain data for a specific detail level.
Run Demo: Drill Down Chart View Example: Create a Drill-Down Chart
<nav aria-label="breadcrumb" class="w-100 breadcrumbs-container">
<ol class="breadcrumb">
@foreach (DrillDownState state in StateList) {
<li class="breadcrumb-item active ">
@if (state != StateList.Last()) {
<a class="breadcrumb-item-link" @onclick="@(() => OnBreadcrumbItemClick(state))">
@state.Name
</a>
}
else {
@state.Name
}
</li>
}
</ol>
</nav>
<DxChart T="SaleItem"
Data="currentState.Data"
Rotated="currentState.Rotated"
Width="100%"
Height="500px"
SeriesClick="OnSeriesClick">
<DxChartTitle Text="DevAV Sales" />
<DxChartCommonSeries T="SaleItem"
TArgument="object"
TValue="double"
TGroup="string"
ArgumentField="currentState.GetArgumentExpression"
NameField="currentState.GetSeriesExpression"
ValueField="si=>si.Income"
SummaryMethod="Enumerable.Sum">
<SeriesTemplate>
@if (currentState.SeriesType == ChartSeriesType.StackedBar) {
<DxChartStackedBarSeries Settings="context"
Color="mainPalette[context.Name]" />
}
@if (currentState.SeriesType == ChartSeriesType.StackedArea) {
<DxChartStackedSplineAreaSeries Settings="context"
Color="mainPalette[context.Name]"
Opacity="1">
<DxChartAggregationSettings Enabled="true"
Method="ChartAggregationMethod.Sum" />
</DxChartStackedSplineAreaSeries>
}
</SeriesTemplate>
</DxChartCommonSeries>
<DxChartLegend HorizontalAlignment="HorizontalAlignment.Center"
VerticalAlignment="VerticalEdge.Bottom"
Position="RelativePosition.Outside"
Orientation="Orientation.Horizontal" />
<DxChartValueAxis>
<DxChartAxisTitle Text="United States Dollars"></DxChartAxisTitle>
</DxChartValueAxis>
<DxChartArgumentAxis ArgumentType="currentState.AxisDataType" />
<DxChartZoomAndPanSettings ArgumentAxisZoomAndPanMode="currentState.ZoomAndPanMode" />
<DxChartScrollBarSettings ArgumentAxisScrollBarPosition="ChartScrollBarPosition.Bottom"
ArgumentAxisScrollBarVisible="currentState.ZoomAndPanMode != ChartAxisZoomAndPanMode.None" />
</DxChart>
public partial class Charts_DrillDown {
List<SaleItem> data;
DrillDownState currentState;
List<DrillDownState> StateList = new List<DrillDownState>();
string preName;
Dictionary<string, Color> mainPalette = new() {
{ "Cameras", Color.FromArgb(255, 253, 204, 109) },
{ "Cell Phones", Color.FromArgb(255, 246, 153, 73) },
{ "Computers", Color.FromArgb(255, 233, 71, 84) },
{ "TV, Audio", Color.FromArgb(255, 148, 11, 63) },
{ "Vehicle Electronics", Color.FromArgb(255, 101, 22, 52) },
{ "Multipurpose Batteries", Color.FromArgb(255, 20, 54, 85) },
{ "Tripod", Color.FromArgb(255, 253, 224, 109) },
{ "Flash", Color.FromArgb(255, 253, 153, 109) },
{ "Camera", Color.FromArgb(255, 253, 206, 109) },
{ "Binoculars", Color.FromArgb(255, 253, 171, 109) },
{ "Camcorder", Color.FromArgb(255, 253, 189, 109) },
{ "Mobile Phone", Color.FromArgb(255, 251, 213, 110) },
{ "Smart Watch", Color.FromArgb(255, 246, 153, 73) },
{ "Smartphone", Color.FromArgb(255, 242, 110, 44) },
{ "Sim Card", Color.FromArgb(255, 249, 185, 90) },
{ "Laptop", Color.FromArgb(255, 245, 111, 106) },
{ "Tablet", Color.FromArgb(255, 217, 50, 73) },
{ "Desktop", Color.FromArgb(255, 233, 71, 84) },
{ "Printer", Color.FromArgb(255, 191, 32, 64) },
{ "Home Audio", Color.FromArgb(255, 221, 74, 102) },
{ "Headphone", Color.FromArgb(255, 165, 25, 80) },
{ "Television", Color.FromArgb(255, 201, 53, 93) },
{ "DVD Player", Color.FromArgb(255, 136, 9, 63) },
{ "Radar", Color.FromArgb(255, 199, 57, 88) },
{ "Car Alarm", Color.FromArgb(255, 143, 33, 69) },
{ "GPS Unit", Color.FromArgb(255, 101, 22, 58) },
{ "Car Accessories", Color.FromArgb(255, 178, 46, 83) },
{ "AC/DC Adapter", Color.FromArgb(255, 49, 150, 172) },
{ "Tester", Color.FromArgb(255, 31, 100, 125) },
{ "Battery", Color.FromArgb(255, 20, 54, 85) },
{ "Converter", Color.FromArgb(255, 39, 124, 148) },
{ "Charger", Color.FromArgb(255, 24, 75, 100) },
};
protected override void OnInitialized() {
data = dataProvider.Generate();
currentState = new DrillDownState("Total Sales",
data,
si => si.Category,
si => si.Company,
true,
ChartAxisZoomAndPanMode.None,
ChartSeriesType.StackedBar,
ChartAxisDataType.String);
StateList.Add(currentState);
}
void OnSeriesClick(ChartSeriesClickEventArgs e) {
IEnumerable<SaleItem> newData;
string name;
if(e.Point != null) {
var pointData = (IEnumerable<SaleItem>)e.Point.DataItems;
var list = pointData.ToList();
list.Sort(); //to make aggregation work correctly
newData = list;
name = $"{e.Series.Name} ({e.Point.Argument})";
} else {
name = e.Series.Name;
if(name == preName)
return;
newData = (IEnumerable<SaleItem>)e.Series.Data;
}
currentState = new DrillDownState(name,
newData,
si => si.Product,
si => si.OrderDate.Date,
false,
ChartAxisZoomAndPanMode.Both,
ChartSeriesType.StackedArea,
ChartAxisDataType.DateTime);
StateList.Add(currentState);
preName = name;
}
void OnBreadcrumbItemClick(DrillDownState state) {
currentState = state;
while(StateList.Last() != currentState)
StateList.Remove(StateList.Last());
preName = state.Name;
}
}
class DrillDownState {
public string Name { get; }
public IEnumerable<SaleItem> Data { get; }
public Expression<Func<SaleItem, string>> GetSeriesExpression { get; }
public Expression<Func<SaleItem, object>> GetArgumentExpression { get; }
public ChartAxisZoomAndPanMode ZoomAndPanMode { get; }
public bool Rotated { get; }
public ChartSeriesType SeriesType { get; }
public ChartAxisDataType AxisDataType { get; }
public DrillDownState(string name,
IEnumerable<SaleItem> data,
Expression<Func<SaleItem, string>> getSeriesExpression,
Expression<Func<SaleItem, object>> getArgumentExpression,
bool rotated,
ChartAxisZoomAndPanMode zoomAndPanMode,
ChartSeriesType seriesType,
ChartAxisDataType axisDataType) {
(Name, Data, GetSeriesExpression, GetArgumentExpression, Rotated, ZoomAndPanMode, SeriesType, AxisDataType) =
(name, data, getSeriesExpression, getArgumentExpression, rotated, zoomAndPanMode, seriesType, axisDataType);
}
}
public class SaleItem : IComparable<SaleItem> {
readonly static string[] companies = new string[] { "DevAV North", "DevAV South", "DevAV West", "DevAV East", "DevAV Central" };
static Dictionary<string, List<string>> categorizedProducts;
internal static Dictionary<string, List<string>> CategorizedProducts {
get {
if(categorizedProducts == null) {
categorizedProducts = new Dictionary<string, List<string>>();
categorizedProducts["Cameras"] = new List<string>()
{"Camera", "Camcorder", "Binoculars", "Flash", "Tripod"};
categorizedProducts["Cell Phones"] = new List<string>()
{"Smartphone", "Mobile Phone", "Smart Watch", "Sim Card"};
categorizedProducts["Computers"] = new List<string>()
{"Desktop", "Laptop", "Tablet", "Printer"};
categorizedProducts["TV, Audio"] = new List<string>()
{"Television", "Home Audio", "Headphone", "DVD Player"};
categorizedProducts["Vehicle Electronics"] = new List<string>()
{"GPS Unit", "Radar", "Car Alarm", "Car Accessories"};
categorizedProducts["Multipurpose Batteries"] = new List<string>()
{"Battery", "Charger", "Converter", "Tester", "AC/DC Adapter"};
}
return categorizedProducts;
}
}
internal static List<SaleItem> GetTotalIncome() {
NonCryptographicRandom rnd = new NonCryptographicRandom(DateTime.Now.Millisecond);
DateTime now = DateTime.Now;
DateTime endDate = new DateTime(now.Year, now.Month, 1);
List<SaleItem> items = new List<SaleItem>();
foreach(string company in companies) {
double companyFactor = rnd.NextDouble() * 0.6 + 1;
foreach(string category in CategorizedProducts.Keys) {
double categoryFactor = rnd.NextDouble() * 0.6 + 1;
foreach(string product in CategorizedProducts[category]) {
int maxIncome = rnd.Next(60, 140);
for(int i = 0; i < 300; i++) {
if(i % 100 == 0)
maxIncome = Math.Max(40, rnd.Next(maxIncome - 20, maxIncome + 20));
DateTime date = endDate.AddDays(-i - 1);
double income = rnd.Next(20, maxIncome) * companyFactor * categoryFactor;
items.Add(new SaleItem() { Category = category, Company = company, Product = product, OrderDate = date, Income = income });
}
}
}
}
return items;
}
public string Product { get; set; }
public string Company { get; set; }
public DateTime OrderDate { get; set; }
public double Income { get; set; }
public string Category { get; set; }
int IComparable<SaleItem>.CompareTo(SaleItem other) {
return OrderDate.CompareTo(other.OrderDate);
}
}