~/cartesianChart/tooltips.md

Tooltips

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

tooltips

Behaviour

On Windows or MacOS you can move the pointer over the chart to display the tooltip, tooltips will be closed when the pointer leaves the chart area.

On Android or iOS slide your finger over the chart to display the tooltip, the tooltip will be closed when the finger goes up.

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.

<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipPosition="Top"><!-- mark -->
</lvc:CartesianChart>
<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipPosition="Bottom"><!-- mark -->
</lvc:CartesianChart>
<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipPosition="Left"><!-- mark -->
</lvc:CartesianChart>
<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipPosition="Right"><!-- mark -->
</lvc:CartesianChart>
<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipPosition="Center"><!-- mark -->
</lvc:CartesianChart>
<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipPosition="Hidden"><!-- mark -->
</lvc:CartesianChart>

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.

<lvc:CartesianChart
    Series="{Binding Series}"
    TooltipFindingStrategy="CompareOnlyX"><!-- mark -->
</lvc:CartesianChart>

Tooltip text

You can define the text the tooltip will display for a given point, using the YToolTipLabelFormatter, XToolTipLabelFormatter or ToolTipLabelFormatter properties, these properties are of type Func<ChartPoint, string> it means that both are a function, that takes a point as parameter and return 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 formatter for every series, all the series have a different formatters, but generally the default value uses the Series.Name and the ChartPoint.Coordinate.PrimaryValue properties, the following code snippet illustrates how to build a custom tooltip formatter.

Lets take the example of the next series:"

public ISeries[] Series { get; set; } = [
    new LineSeries<double>
    {
        Values = [2, 1, 3, 5, 3, 4, 6],
        Fill = null,
        GeometrySize = 20,
    },
    new LineSeries<int, StarGeometry>
    {
        Values = [4, 2, 5, 2, 4, 5, 3],
        Fill = null,
        GeometrySize = 20
    }
];

By default the tooltip will be:

tooltip

We can add format to the tooltip:

public ISeries[] Series { get; set; } = [
    new LineSeries<double>
    {
        Values = [2, 1, 3, 5, 3, 4, 6],
        Fill = null,
        GeometrySize = 20,
        YToolTipLabelFormatter = point => point.Model.ToString("N2") // mark
    },
    new LineSeries<int, StarGeometry>
    {
        Values = [4, 2, 5, 2, 4, 5, 3],
        Fill = null,
        GeometrySize = 20,
        YToolTipLabelFormatter = point => point.Model.ToString("N2") // mark
    }
];

tooltip

We used the Model property of the point, the Model property is just the item in the Values collection, for example in the next case, the Model property is of type City.

public ISeries[] Series { get; set; } = [
    new LineSeries<City>
    {
        Values = [new() { Population = 4 }, new() { Population = 2}],
        YToolTipLabelFormatter = point => point.Model.Population.ToString("N2") // mark
    }
];

// ...

public class City
{
    public double Population { get; set; }
}

We can also show a label for the X coordinate, the default tooltip uses the X label as the header in the tooltip.

new LineSeries<double>
{
    Values = [2, 1, 3, 5, 3, 4, 6],
    Fill = null,
    GeometrySize = 20,
    XToolTipLabelFormatter = point => point.Index.ToString(), // mark
    YToolTipLabelFormatter = point => point.Model.ToString("C2")
};

tooltip

When the series is "Stacked" (PieSeries, StackedColumn or StackedRow) we can find information about the stacked data in the StackedValue property, for example:

public ISeries[] Series { get; set; } = [
    new StackedColumnSeries<double>
    {
        Values = [2, 1, 3, 5, 3, 4, 6],
        YToolTipLabelFormatter =
            point => $"{point.Model} / {point.StackedValue!.Total} ({point.StackedValue.Share:P2})"
    },
    new StackedColumnSeries<int>
    {
        Values = [4, 2, 5, 2, 4, 5, 3],
        YToolTipLabelFormatter =
            point => $"{point.Model} / {point.StackedValue!.Total} ({point.StackedValue.Share:P2})"
    }
];

Will result in:

tooltip

The PieSeries class uses the ToolTipLabelFormatter property to configure the text inside the tooltip.

Customize default tooltips

You can quickly change the position, the font, the text size or the background color:

View

<UserControl x:Class="WPFSample.Axes.NamedLabels.View"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
             xmlns:vms="clr-namespace:ViewModelsSamples.Axes.NamedLabels;assembly=ViewModelsSamples">
    <UserControl.DataContext>
        <vms:ViewModel/>
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <lvc:CartesianChart
            Series="{Binding Series}"
            XAxes="{Binding XAxes}"
            YAxes="{Binding YAxes}"
            TooltipPosition="Left"
            TooltipBackgroundPaint="{Binding TooltipBackgroundPaint}"
            TooltipTextPaint="{Binding TooltipTextPaint}"
            TooltipTextSize="16">
        </lvc:CartesianChart>
    </Grid>
</UserControl>

View model

[ObservableObject]
public partial class ViewModel
{
    public ISeries[] Series { get; set; } = { ... };
    public Axis[] XAxes { get; set; } = { ... };
    public Axis[] YAxes { get; set; } = { ... };

    public SolidColorPaint TooltipTextPaint { get; set; } = // mark
        new SolidColorPaint // mark
        { // mark
            Color = new SKColor(242, 244, 195), // mark
            SKTypeface = SKTypeface.FromFamilyName("Courier New") // mark
        }; // mark

    public SolidColorPaint TooltipBackgroundPaint { get; set; } = // mark
        new SolidColorPaint(new SKColor(72, 0, 50)); // mark
}

image

Custom tooltip control

You can also create your own tooltip, the recommended way is to use the LiveCharts API (example bellow) but you can use anything as tooltip as soon as it implements the IChartTooltip<T> interface. In the following example we build a custom control to render tooltips in out charts using the LiveCharts API.

The next tooltip is drawn by the library, LiveCharts can only draw inside the control bounds, in some cases it could cause issues like #912.

Alternatively, you can build your own Tooltips and use the power of your UI framework, see #1558 for more info.

CustomTooltip.cs

using System;
using System.Collections.Generic;
using LiveChartsCore;
using LiveChartsCore.Drawing;
using LiveChartsCore.Kernel;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.Measure;
using LiveChartsCore.SkiaSharpView.Drawing;
using LiveChartsCore.SkiaSharpView.Drawing.Geometries;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.VisualElements;
using LiveChartsCore.VisualElements;
using SkiaSharp;

namespace ViewModelsSamples.General.TemplatedTooltips;

public class CustomTooltip : IChartTooltip<SkiaSharpDrawingContext>
{
    private StackPanel<RoundedRectangleGeometry, SkiaSharpDrawingContext>? _stackPanel;
    private static readonly int s_zIndex = 10100;
    private readonly SolidColorPaint _backgroundPaint = new(new SKColor(28, 49, 58)) { ZIndex = s_zIndex };
    private readonly SolidColorPaint _fontPaint = new(new SKColor(230, 230, 230)) { ZIndex = s_zIndex + 1 };

    public void Show(IEnumerable<ChartPoint> foundPoints, Chart<SkiaSharpDrawingContext> chart)
    {
        if (_stackPanel is null)
        {
            _stackPanel = new StackPanel<RoundedRectangleGeometry, SkiaSharpDrawingContext>
            {
                Padding = new Padding(25),
                Orientation = ContainerOrientation.Vertical,
                HorizontalAlignment = Align.Start,
                VerticalAlignment = Align.Middle,
                BackgroundPaint = _backgroundPaint
            };

            _stackPanel
                .Animate(
                    new Animation(EasingFunctions.BounceOut, TimeSpan.FromSeconds(1)),
                    nameof(_stackPanel.X),
                    nameof(_stackPanel.Y));
        }

        // clear the previous elements.
        foreach (var child in _stackPanel.Children.ToArray())
        {
            _ = _stackPanel.Children.Remove(child);
            chart.RemoveVisual(child);
        }

        foreach (var point in foundPoints)
        {
            var skiaSeries = (IChartSeries<SkiaSharpDrawingContext>)point.Context.Series;

            var label = new LabelVisual
            {
                Text = point.Coordinate.PrimaryValue.ToString("C2"),
                Paint = _fontPaint,
                TextSize = 15,
                Padding = new Padding(8, 0, 0, 0),
                ClippingMode = ClipMode.None, // required on tooltips // mark
                VerticalAlignment = Align.Start,
                HorizontalAlignment = Align.Start
            };

            var sp = new StackPanel<RoundedRectangleGeometry, SkiaSharpDrawingContext>
            {
                Padding = new Padding(0, 4),
                VerticalAlignment = Align.Middle,
                HorizontalAlignment = Align.Middle,
                Children =
                {
                    skiaSeries.GetMiniature(point, s_zIndex),
                    label
                }
            };

            _stackPanel.Children.Add(sp);
        }

        var size = _stackPanel.Measure(chart);

        var location = foundPoints.GetTooltipLocation(size, chart);

        _stackPanel.X = location.X;
        _stackPanel.Y = location.Y;

        chart.AddVisual(_stackPanel);
    }

    public void Hide(Chart<SkiaSharpDrawingContext> chart)
    {
        if (chart is null || _stackPanel is null) return;
        chart.RemoveVisual(_stackPanel);
    }
}

View

<UserControl x:Class="WPFSample.General.TemplatedTooltips.View"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
             xmlns:vms="clr-namespace:ViewModelsSamples.General.TemplatedTooltips;assembly=ViewModelsSamples"
             xmlns:ctx="clr-namespace:LiveChartsCore.Kernel;assembly=LiveChartsCore">
    <UserControl.DataContext>
        <vms:ViewModel/>
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <lvc:CartesianChart Grid.Row="0" Series="{Binding Series}" TooltipPosition="Top" >
            <!-- mark -untilCloses CartesianChart.Tooltip -->
            <lvc:CartesianChart.Tooltip>
                <vms:CustomTooltip></vms:CustomTooltip>
            </lvc:CartesianChart.Tooltip>
        </lvc:CartesianChart>
    </Grid>
</UserControl>

custom tooltip