Line Series Props
This article do not include all the properties of the Line Series Props class, it only highlights some features, to explore the full object checkout the API explorer
Name property
The name property is a string identifier that is normally used in tooltips and legends to display the data name, if this property is not set, then the library will generate a name for the series that by default is called "Series 1" when it is the first series in the series collection, "Series 2" when it is the second series in the series collection, "Series 3" when it is the third series in the series collection, and so on a series n will be named "Series n".
SeriesCollection = new ISeries[]
{
new LineSeriesProps<int>
{
Values = new []{ 2, 5, 4, 2, 6 },
Name = "Income", // mark
Stroke = null
},
new LineSeriesProps<int>
{
Values = new []{ 3, 7, 2, 9, 4 },
Name = "Outcome", // mark
Stroke = null
}
};
Values property
The Values
property is of type IEnumerable<T>
, this means that you can use any object that implements the IEnumerable<T>
interface,
such as Array
, List<T>
or ObservableCollection<T>
, this property contains the data to plot, you can use any type as the
generic argument (<T>
) as soon as you let the library how to handle it, the library already knows how to handle multiple types,
but you can register any type and teach the library how to handle any object in a chart, for more information please see the
mappers article.
var series1 = new LineSeriesProps<int>
{
Values = new List<int> { 2, 1, 3 }
};
// == Update the chart when a value is added, removed or replaced == // mark
// using ObservableCollections allows the chart to update
// every time you add a new element to the values collection
// (not needed in Blazor, it just... updates)
var series2 = new LineSeriesProps<double>
{
Values = new ObservableCollection<double> { 2, 1, 3 }
}
series2.add(4); // and the chart will animate the change!
// == Update the chart when a property in our collection changes == // mark
// if the object implements INotifyPropertyChanged, then the chart will
// update automatically when a property changes, the library already provides
// many 'ready to go' objects such as the ObservableValue class.
var observableValue = new ObservableValue(5);
var series3 = new LineSeriesProps<ObservableValue>
{
Values = new ObservableCollection<ObservableValue> { observableValue },
}
observableValue.Value = 9; // the chart will animate the change from 5 to 9!
// == Passing X and Y coordinates // mark
// you can indicate both, X and Y using the Observable point class.
// or you could define your own object using mappers.
var series4 = new LineSeriesProps<ObservablePoint>
{
Values = new ObservableCollection<ObservablePoint> { new ObservablePoint(2, 6)}
}
// == Custom types and mappers == // mark
// finally you can also use your own object, take a look at the City class.
public class City
{
public string Name { get; set; }
public double Population { get; set; }
}
// we must let the series know how to handle the city class.
// use the Mapping property to build a point from the city class
// you could also register the map globally.
// for more about global mappers info see:
// https://lvcharts.com/docs/blazor/2.0.0-beta.710/Overview.Mappers
var citiesSeries = new LineSeriesProps<City>
{
Values = new City[]
{
new City { Name = "Tokio", Population = 9 },
new City { Name = "New York", Population = 11 },
new City { Name = "Mexico City", Population = 10 },
},
Mapping = (city, point) =>
{
// this function will be called for every city in our data collection
// in this case Tokio, New York and Mexico city
// it takes the city and the point in the chart liveCharts built for the given city
// you must map the coordinates to the point
// use the Population property as the primary value (normally Y)
point.PrimaryValue = (float)city.Population;
// use the index of the city in our data collection as the secondary value
// (normally X)
point.SecondaryValue = point.Context.Index;
}
};
Automatic updates do not have a significant performance impact in most of the cases!
Data labels
Data labels are labels for every point in a series, there are multiple properties to customize them, take a look at the following sample:
new LineSeriesProps<double>
{
DataLabelsSize = 20,
DataLabelsPaint = new SolidColorPaint(SKColors.Blue),
// all the available positions at:
// https://lvcharts.com/api/2.0.0-beta.710/LiveChartsCore.Measure.DataLabelsPosition
DataLabelsPosition = LiveChartsCore.Measure.DataLabelsPosition.Top,
// The DataLabelsFormatter is a function
// that takes the current point as parameter
// and returns a string.
// in this case we returned the PrimaryValue property as currency
DataLabelsFormatter = (point) => point.PrimaryValue.ToString("C2"),
Values = new ObservableCollection<double> { 2, 1, 3, 5, 3, 4, 6 },
Fill = null
}
The previous series will result in the following chart:
Stroke property
If the stroke property is not set, then LiveCharts will create it based on the series position in your series collection and the current theme.
Series = new ISeries[]
{
new LineSeries<int>
{
Values = new [] { 4, 4, 7, 2, 8 },
Stroke = new SolidColorPaint(SKColors.Blue) { StrokeThickness = 4 }, // mark
Fill = null,
GeometryFill = null,
GeometryStroke = null
},
new LineSeries<int>
{
Values = new [] { 7, 5, 3, 2, 6 },
Stroke = new SolidColorPaint(SKColors.Red) { StrokeThickness = 8 }, // mark
Fill = null,
GeometryFill = null,
GeometryStroke = null
},
new LineSeries<int>
{
Values = new [] { 4, 2, 5, 3, 9 },
Stroke = new SolidColorPaint(SKColors.Green) { StrokeThickness = 1 }, // mark
Fill = null,
GeometryFill = null,
GeometryStroke = null
}
};
Paints can create gradients, dashed lines and more, if you need help using the Paint
instances take
a look at the Paints article.
Fill property
If the fill property is not set, then LiveCharts will create it based on the series position in your series collection and the current theme.
The alpha channel enables transparency, it goes from 0 to 255, 0 is transparent and 255 disables transparency completely.
Series = new ISeries[]
{
new LineSeries<int>
{
Values = new [] { 4, 4, 7, 2, 8 },
Fill = new SolidColorPaint(SKColors.Blue.WithAlpha(90)), // mark
Stroke = null,
GeometryFill = null,
GeometryStroke = null
},
new LineSeries<int>
{
Values = new [] { 7, 5, 3, 2, 6 },
Fill = new SolidColorPaint(SKColors.Red.WithAlpha(90)), // mark
Stroke = null,
GeometryFill = null,
GeometryStroke = null
},
new LineSeries<int>
{
Values = new [] { 4, 2, 5, 3, 9 },
Fill = new SolidColorPaint(SKColors.Green.WithAlpha(90)), // mark
Stroke = null,
GeometryFill = null,
GeometryStroke = null
}
};
Paints can create gradients, dashed lines and more, if you need help using the Paint
instances take
a look at the Paints article.
GeometryFill and GeometryStroke properties
The geometry is the circle shape (by default) that the line series draws for every point, you can customize the fill and stroke of this shape, if none of these properties are set then LiveCharts will create them based on the series position in your series collection and the current theme.
Series = new ISeries[]
{
new LineSeries<int>
{
Values = new [] { 4, 4, 7, 2, 8 },
Stroke = new SolidColorPaint(SKColors.Blue) { StrokeThickness = 4 },
Fill = null,
GeometryFill = new SolidColorPaint(SKColors.AliceBlue), // mark
GeometryStroke = new SolidColorPaint(SKColors.Gray) { StrokeThickness = 4 } // mark
},
new LineSeries<int>
{
Values = new [] { 7, 5, 3, 2, 6 },
Stroke = new SolidColorPaint(SKColors.Red) { StrokeThickness = 8 },
Fill = null,
GeometryFill = new SolidColorPaint(SKColors.IndianRed), // mark
GeometryStroke = new SolidColorPaint(SKColors.DarkSalmon) { StrokeThickness = 8 } // mark
}
};
Paints can create gradients, dashed lines and more, if you need help using the Paint
instances take
a look at the Paints article.
GeometrySize property
Determines the size of the geometry, if this property is not set, then the library will decide it based on the theme.
Series = new ISeries[]
{
new LineSeries<int>
{
Values = new [] { 4, 4, 7, 2, 8 },
GeometrySize = 10 // mark
},
new LineSeries<int>
{
Values = new [] { 7, 5, 3, 2, 6 },
GeometrySize = 30 // mark
}
};
LineSmoothness property
Determines if the series line is straight or curved, this property is of type double
and goes from 0
to 1
any other
value will be ignored, where 0 is straight and 1 is the most curved line.
Series = new ISeries[]
{
new LineSeries<int>
{
Values = new [] { 5, 4, 7, 3, 8 },
LineSmoothness = 0 // mark
},
new LineSeries<int>
{
Values = new [] { 7, 2, 6, 2, 6 },
LineSmoothness = 1 // mark
}
};
EnableNullSplitting property
This property is enabled by default (true
), it has a performance cost and allows the series to create gaps, when the
series finds a null
instance then the series will create a gap.
Series = new ISeries[]
{
new LineSeries<int?>
{
Values = new int?[]
{
5,
4,
2,
null, // mark
3,
8,
6
},
LineSmoothness = 0
}
};
Plotting custom types
You can teach LiveCharts to plot anything, imagine the case where we have an array of the City
class defined bellow:
public class City
{
public string Name { get; set; }
public double Population { get; set; }
public double LandArea { get; set; }
}
You can register this type globally, this means that every time LiveCharts finds a City
instance in a chart
it will use the mapper we registered, global mappers are unique for a type, if you need to plot multiple
properties then you should use local mappers.
// Ideally you should call this when your application starts
// If you need help to decide where to add this code
// please see the installation guide in this docs.
// in this case we have an array of the City class
// we need to compare the Population property of every city in our array
LiveCharts.Configure(config =>
config
.HasMap<City>((city, point) =>
{
// in this lambda function we take an instance of the City class (see city parameter)
// and the point in the chart for that instance (see point parameter)
// LiveCharts will call this method for every instance of our City class array,
// now we need to populate the point coordinates from our City instance to our point
// in this case we will use the Population property as our primary value (normally the Y coordinate)
point.PrimaryValue = (float)city.Population;
// then the secondary value (normally the X coordinate)
// will be the index of city in our cities array
point.SecondaryValue = point.Context.Index;
// but you can use another property of the city class as the X coordinate
// for example lets use the LandArea property to create a plot that compares
// Population and LandArea in chart:
// point.SecondaryValue = (float)city.LandArea;
})
.HasMap<Foo>(...) // you can register more types here using our fluent syntax
.HasMap<Bar>(...)
);
Now we are ready to plot cities all over our application:
Series = new[]
{
new LineSeries<City>
{
Name = "Population",
TooltipLabelFormatter = point => $"{point.Model.Name} {point.PrimaryValue:N2}M",
Values = new[]
{
new City { Name = "Tokyo", Population = 4, LandArea = 3 },
new City { Name = "New York", Population = 6, LandArea = 4 },
new City { Name = "Seoul", Population = 2, LandArea = 1 },
new City { Name = "Moscow", Population = 8, LandArea = 7 },
new City { Name = "Shanghai", Population = 3, LandArea = 2 },
new City { Name = "Guadalajara", Population = 4, LandArea = 5 }
}
}
};
Alternatively you could create a local mapper that will only work for a specific series, global mappers will be
ignored when the series Mapping
property is not null.
var cities = new[]
{
new City { Name = "Tokyo", Population = 4, LandArea = 3 },
new City { Name = "New York", Population = 6, LandArea = 4 },
new City { Name = "Seoul", Population = 2, LandArea = 1 },
new City { Name = "Moscow", Population = 8, LandArea = 7 },
new City { Name = "Shanghai", Population = 3, LandArea = 2 },
new City { Name = "Guadalajara", Population = 4, LandArea = 5 }
};
Series = new[]
{
// this series draws the Population property in the Y axis
new LineSeries<City>
{
Name = "Population",
TooltipLabelFormatter = point => $"{point.Model.Name} population: {point.PrimaryValue:N2}M",
Values = cities,
Mapping = (city, point) =>
{
point.PrimaryValue = (float)city.Population;
point.SecondaryValue = point.Context.Index;
}
},
// draws the LandArea property in the Y axis
new LineSeries<City>
{
Name = "Population",
TooltipLabelFormatter = (point) => $"{point.Model.Name} area: {point.PrimaryValue:N2}KM2",
Values = cities,
Mapping = (city, point) =>
{
point.PrimaryValue = (float)city.LandArea;
point.SecondaryValue = point.Context.Index;
}
},
// compares Population (Y) and LandArea (Y)
//new LineSeries<City>
//{
// Name = "Population",
// TooltipLabelFormatter = (point) => $"{point.Model.Name} population: {point.PrimaryValue:N2}M, area: {point.SecondaryValue}KM2",
// Values = cities,
// Mapping = (city, point) =>
// {
// point.PrimaryValue = (float)city.Population;
// point.SecondaryValue = (float)city.LandArea;
// }
//}
};
Custom geometries
You can use any geometry to represent a point in a line series.
Series = new List<ISeries>
{
// use the second argument type to specify the geometry to draw for every point
// there are already many predefined geometries in the
// LiveChartsCore.SkiaSharpView.Drawing.Geometries namespace
new LineSeries<double, LiveChartsCore.SkiaSharpView.Drawing.Geometries.RectangleGeometry>
{
Values = new List<double> { 3, 3, -3, -2, -4, -3, -1 },
Fill = null,
LineSmoothness = 1
},
// you can also define your own SVG geometry
new LineSeries<double, MyGeometry>
{
Values = new List<double> { -2, 2, 1, 3, -1, 4, 3 },
Stroke = new SolidColorPaint(SKColors.DarkOliveGreen, 3),
Fill = null,
GeometryStroke = null,
GeometryFill = new SolidColorPaint(SKColors.DarkOliveGreen),
GeometrySize = 40
}
};
Where MyGeometry
class is our custom shape, you can draw anything SkiaSharp
supports at this point,
but in this case we will draw an SVG path, we inherit from SVGPathGeometry
, and for performance reasons
we use a static variable to parse the SVG path, this ways the parse operation only runs once.
public class MyGeometry : LiveChartsCore.SkiaSharpView.Drawing.Geometries.SVGPathGeometry
{
// the static field is important to prevent the svg path is parsed multiple times // mark
// Icon from Google Material Icons font.
// https://fonts.google.com/icons?selected=Material%20Icons%20Outlined%3Atask_alt%3A
public static SKPath svgPath = SKPath.ParseSvgPathData(
"M22,5.18L10.59,16.6l-4.24-4.24l1.41-1.41l2.83,2.83l10-10L22,5.18z M19.79,10.22C19.92,10.79,20,11.39,20,12 " +
"c0,4.42-3.58,8-8,8s-8-3.58-8-8c0-4.42,3.58-8,8-8c1.58,0,3.04,0.46,4.28,1.25l1.44-1.44C16.1,2.67,14.13,2,12,2C6.48,2,2,6.48,2,12 " +
"c0,5.52,4.48,10,10,10s10-4.48,10-10c0-1.19-0.22-2.33-0.6-3.39L19.79,10.22z");
public MyGeometry()
: base(svgPath)
{
}
public override void OnDraw(SkiaSharpDrawingContext context, SKPaint paint)
{
// lets also draw a white circle as background before the svg path is drawn
// this will just make things look better
using (var backgroundPaint = new SKPaint())
{
backgroundPaint.Style = SKPaintStyle.Fill;
backgroundPaint.Color = SKColors.White;
var r = Width / 2;
context.Canvas.DrawCircle(X + r, Y + r, r, backgroundPaint);
}
base.OnDraw(context, paint);
}
}
ZIndex property
Indicates an order in the Z axis, this order controls which series is above or behind.
IsVisible property
Indicates if the series is visible in the user interface.
DataPadding
The data padding is the minimum distance from the edges of the series to the axis limits, it is of type System.Drawing.PointF
both coordinates (X and Y) goes from 0 to 1, where 0 is nothing and 1 is the axis tick an axis tick is the separation between
every label or separator (even if they are not visible).
If this property is not set, the library will set it according to the series type, take a look at the following samples:
new LineSeries<double>
{
DataPadding = new System.Drawing.PointF(0, 0),
Values = new ObservableCollection { 2, 1, 3, 5, 3, 4, 6 },
GeometryStroke = null,
GeometryFill = null,
Fill = null
}
Produces the following result:
But you can remove the padding only from an axis, for example:
new LineSeries<double>
{
DataPadding = new System.Drawing.PointF(0.5f, 0),
Values = new ObservableCollection<double> { 2, 1, 3, 5, 3, 4, 6 },
GeometryStroke = null,
GeometryFill = null,
Fill = null
}
Or you can increase the distance:
new LineSeries<double>
{
DataPadding = new System.Drawing.PointF(2, 2),
Values = new ObservableCollection<double> { 2, 1, 3, 5, 3, 4, 6 },
GeometryStroke = null,
GeometryFill = null,
Fill = null
}