~/cartesianChart/tooltips.md

Tooltips

Tooltips are popups that help the user to read a chart as the pointer moves.

tooltips

TooltipPosition property

You can place a tooltip at Top, Bottom, Left, Right, Center or Hidden positions, for now tooltips for the PieChart class only support the Center position, default value is Top.

Notice the Hidden position will disable tooltips in a chart.

cartesianChart1.Series = new ISeries[] { new LineSeries<int> { Values = new[] { 2, 5, 4 } } };
cartesianChart1.TooltipPosition = LiveChartsCore.Measure.TooltipPosition.Bottom;

TooltipFindingStrategy property

Every point drawn by the library defines a HoverArea, it defines an area in the chart that "triggers" the point, it is specially important to fire tooltips, a point will be included in a tooltip when the hover area was triggered by the pointer position.

The TooltipFindingStrategy property determines the hover area planes (X or Y) that a chart will use to trigger the HoverArea instances. In a chart, the following options are available:

CompareAll: Selects all the points whose hover area contain the pointer position.

tooltips

CompareOnlyX: Selects all the points whose hover area contain the pointer position, but it ignores the Y plane.

tooltips

CompareOnlyY: Selects all the points whose hover area contain the pointer position, but it ignores the X plane.

tooltips

CompareAllTakeClosest: Selects all the points whose hover area contain the pointer position, it only takes the closest point to the pointer, one per series.

CompareOnlyXTakeClosest: Selects all the points whose hover area contain the pointer position, but it ignores the Y plane, it only takes the closest point to the pointer, one per series.

CompareOnlyYTakeClosest: Selects all the points whose hover area contain the pointer position, but it ignores the X plane, it only takes the closest point to the pointer, one per series.

Automatic (default): Based on the series in the chart, LiveCharts will determine a finding strategy (one of the previous mentioned), all the series have a preferred finding strategy, normally vertical series prefer the CompareOnlyXTakeClosest strategy, horizontal series prefer CompareOnlyYTakeClosest, and scatter series prefers CompareAllTakeClosest, if all the series prefer the same strategy, then that strategy will be selected for the chart, if any series differs then the CompareAllTakeClosest strategy will be used.

Notice that the Axis.UnitWidth property might affect the tooltips in DateTime scaled charts, ensure your chart axis is using the properly unit width.

cartesianChart1.Series = new ISeries[] { new LineSeries<int> { Values = new[] { 2, 5, 4 } } };
cartesianChart1.TooltipFindingStrategy = LiveChartsCore.Measure.TooltipFindingStrategy.CompareOnlyX;

Tooltip point text

You can define the text the tooltip will display for a given point, using the Series.TooltipLabelFormatter property, this property is of type Func<ChartPoint, string> this means that is is a function, that takes a point as parameter and returns a string, the point will be injected by LiveCharts in this function to get a string out of it when it requires to build the text for a point in a tooltip, the injected point will be different as the user moves the pointer over the user interface.

By default the library already defines a default TooltipLabelFormatter for every series, all the series have a different formatter, but generally the default value uses the Series.Name and the ChartPoint.PrimaryValue properties, the following code snippet illustrates how to build a custom tooltip formatter.

new LineSeries<double>
{
    Name = "Sales",
    Values = new ObservableCollection<double> { 200, 558, 458 },
    // for the following formatter
    // when the pointer is over the first point (200), the tooltip will display:
    // Sales: 200
    TooltipLabelFormatter =
        (chartPoint) => $"{chartPoint.Context.Series.Name}: {chartPoint.PrimaryValue}"
},

new ColumnSeries<double>
{
    Name = "Sales 2",
    Values = new ObservableCollection<double> { 250, 350, 240 },
    // now it will use a currency formatter to display the primary value
    // result: Sales 2: $200.00
    TooltipLabelFormatter =
        (chartPoint) => $"{chartPoint.Context.Series.Name}: {chartPoint.PrimaryValue:C2}"
},

new StepLineSeries<ObservablePoint>
{
    Name = "Average",
    Values = new ObservableCollection<ObservablePoint>
    {
        new ObservablePoint(10, 5),
        new ObservablePoint(5, 8)
    },
    // We can also display both coordinates (X and Y in a cartesian coordinate system)
    // result: Average: 10, 5
    TooltipLabelFormatter =
        (chartPoint) => $"{chartPoint.Context.Series.Name}: {chartPoint.SecondaryValue}, {chartPoint.PrimaryValue}"
},

new ColumnSeries<ObservablePoint>
{
    Values = new ObservableCollection<double> { 250, 350, 240 },
    // or anything...
    // result: Sales at this moment: $200.00
    TooltipLabelFormatter =
        (chartPoint) => $"Sales at this moment: {chartPoint.PrimaryValue:C2}"
}

Styling tooltips

A chart exposes many properties to quickly style a tooltip:

cartesianChart1.TooltipPosition = LiveChartsCore.Measure.TooltipPosition.Left;
cartesianChart1.TooltipFont = new System.Drawing.Font("Courier New", 25);
cartesianChart1.TooltipTextColor = System.Drawing.Color.FromArgb(255, 242, 244, 195);
cartesianChart1.TooltipBackColor = System.Drawing.Color.FromArgb(255, 72, 0, 50);

The code above would result in the following tooltip:

zooming

Custom template

You can create your own tooltip control, the key is that your control must implement IChartTooltip<SkiaSharpDrawingContext> and then you have to create a new instance of that control when your chart initializes.

Add a new form to your app named CustomTooltip, then change the code behind as follows:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using LiveChartsCore;
using LiveChartsCore.Drawing;
using LiveChartsCore.Kernel;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.SkiaSharpView.Drawing;
using LiveChartsCore.SkiaSharpView.WinForms;

namespace WinFormsSample.General.TemplatedTooltips;

public partial class CustomTooltip : Form, IChartTooltip<SkiaSharpDrawingContext>, IDisposable
{
    public CustomTooltip()
    {
        InitializeComponent();
    }

    public void Show(IEnumerable<ChartPoint> tooltipPoints, Chart<SkiaSharpDrawingContext> chart)
    {
        var wfChart = (Chart)chart.View;

        var size = DrawAndMesure(tooltipPoints, wfChart);
        LvcPoint? location = null;

        if (chart is CartesianChart<SkiaSharpDrawingContext>)
        {
            location = tooltipPoints.GetCartesianTooltipLocation(
                chart.TooltipPosition, new LvcSize((float)size.Width, (float)size.Height), chart.ControlSize);
        }
        if (chart is PieChart<SkiaSharpDrawingContext>)
        {
            location = tooltipPoints.GetPieTooltipLocation(
                chart.TooltipPosition, new LvcSize((float)size.Width, (float)size.Height));
        }

        BackColor = Color.FromArgb(255, 30, 30, 30);
        Height = (int)size.Height;
        Width = (int)size.Width;

        var l = wfChart.PointToScreen(Point.Empty);
        var x = l.X + (int)location.Value.X;
        var y = l.Y + (int)location.Value.Y;
        Location = new Point(x, y);
        Show();

        wfChart.CoreCanvas.Invalidate();
    }

    private SizeF DrawAndMesure(IEnumerable<ChartPoint> tooltipPoints, Chart chart)
    {
        SuspendLayout();
        Controls.Clear();

        var h = 0f;
        var w = 0f;
        foreach (var point in tooltipPoints)
        {
            using var g = CreateGraphics();
            var text = point.AsTooltipString;
            var size = g.MeasureString(text, chart.TooltipFont);

            var drawableSeries = (IChartSeries<SkiaSharpDrawingContext>)point.Context.Series;

            Controls.Add(new MotionCanvas
            {
                Location = new Point(6, (int)h + 6),
                PaintTasks = drawableSeries.CanvasSchedule.PaintSchedules,
                Width = (int)drawableSeries.CanvasSchedule.Width,
                Height = (int)drawableSeries.CanvasSchedule.Height
            });
            Controls.Add(new Label
            {
                Text = text,
                ForeColor = Color.FromArgb(255, 250, 250, 250),
                Font = chart.TooltipFont,
                Location = new Point(6 + (int)drawableSeries.CanvasSchedule.Width + 6, (int)h + 6),
                AutoSize = true
            });

            var thisW = size.Width + 18 + (int)drawableSeries.CanvasSchedule.Width;
            h += size.Height + 6;
            w = thisW > w ? thisW : w;
        }

        h += 6;

        ResumeLayout();
        return new SizeF(w, h);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components is not null))
        {
            components.Dispose();
        }

        base.Dispose(disposing);
    }
}

Your tooltip is ready to be used, now when you create a chart, we have to pass a new instance of the tooltip we just created.

var cartesianChart = new CartesianChart(tooltip: new CustomTooltip())
{
    Series = viewModel.Series
};

You can find a another example at the source code of the DefaultTooltip class (xaml, code).

custom tooltip