How to Create Draggable Marker Lines in MAUI with LiveCharts

Learn how to implement interactive, draggable marker lines in your .NET MAUI charts using LiveCharts2 by binding PressedCommand and MovedCommand.

  1. In the XAML file, add PressedCommand, MovedCommand, and ReleasedCommand bindings, and set AnimationsSpeed="0:0:0.02" and EasingFunction="{x:Null}" (you can try other animation effects).

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    <lvc:CartesianChart 
        PressedCommand="{Binding PressedCommand}" 
        MovedCommand="{Binding MovedCommand}" 
        ReleasedCommand="{Binding ReleasedCommand}" 
        AnimationsSpeed="0:0:0.02" 
        EasingFunction="{x:Null}"
        Series="{Binding Series}"
        Sections="{Binding Sections}"
        XAxes="{Binding XAxes}"
        YAxes="{Binding YAxes}">
    </lvc:CartesianChart>
    
  2. In the .cs file, add command-related methods.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    public Axis[] XAxes { get; set; } =
    {
        new Axis
        {
            Name = "Crosshair",
            MinLimit = 0,
            MaxLimit = 10,
            Labeler = value => value.ToString("N2")
        }
    };
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    public RectangularSection[] Sections { get; set; } =
    {
        new RectangularSection
        {
            Yi = 350,
            Yj = 350,
            Stroke = new SolidColorPaint
            {
                Color = SKColors.Red,
                StrokeThickness = 3,
                PathEffect = new LiveChartsCore.SkiaSharpView.Painting.Effects.DashEffect(new float[] { 6, 6 })
            }
        },
        new RectangularSection
        {
            LabelPaint = new SolidColorPaint { Color = SKColors.Blue.WithAlpha(20) },
            LabelSize=20,
            Label = "x2",
            Xi = 4,
            Xj = 6,
            Fill = new SolidColorPaint { Color = SKColors.Blue.WithAlpha(20) }
        },
        new RectangularSection
        {
            LabelPaint = new SolidColorPaint { Color = SKColors.Blue },
            LabelSize=20,
            Label = "x1",
            Xi = 1.2,
            Xj = 1.2,
            Stroke = new SolidColorPaint
            {
                Color = SKColors.Red,
                StrokeThickness = 3,
            },
        },
        new RectangularSection
        {
            LabelPaint = new SolidColorPaint { Color = SKColors.Blue },
            LabelSize=20,
            Label = "x2",
            Xi = 2.2,
            Xj = 2.2,
            Stroke = new SolidColorPaint
            {
                Color = SKColors.Red,
                StrokeThickness = 1,
                PathEffect = new LiveChartsCore.SkiaSharpView.Painting.Effects.DashEffect(new float[] { 6, 6 })
            },
        },
    };
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    
    [RelayCommand]
        private void Pressed(LiveChartsCore.Kernel.Events.PointerCommandArgs pointerCommandArgs)
        {        
            if (_rectangularSection != null)
            {
                var axes = XAxes.First();
                axes.CrosshairPaint = new SolidColorPaint(SKColors.Transparent, 1);
                axes.CrosshairLabelsPaint = new SolidColorPaint(SKColors.DarkRed, 1);
                axes.CrosshairLabelsBackground = SKColors.DarkOrange.AsLvcColor();
            }
        }
    
        [RelayCommand]
        private void Moved(LiveChartsCore.Kernel.Events.PointerCommandArgs pointerCommandArgs)
        {
            var chart = (ICartesianChartView<SkiaSharpDrawingContext>)pointerCommandArgs.Chart;
            var scaledPoint = chart.ScalePixelsToData(pointerCommandArgs.PointerPosition);
            var v = Sections.Where(s => s.Xi * 0.995 <= scaledPoint.X && s.Xi * 1.005 >= scaledPoint.X).ToList();
    
            var axes = XAxes.First();
            pressed = pointerCommandArgs.GetPressStatus();
            if (!pressed)
            {
                if (v.Count > 0)
                {
                    _rectangularSection = v.First();
                    Application.Current?.MainPage.SetCustomCursor(CursorIcon.SizeWestEast, Application.Current?.MainPage.Handler.MauiContext);
                }
                else
                {
                    if (_rectangularSection != null)
                    {
                        _rectangularSection = null;
                    }
                    Application.Current?.MainPage.SetCustomCursor(CursorIcon.Arrow, Application.Current?.MainPage.Handler.MauiContext);
                }
                axes.CrosshairPaint = null;
                axes.CrosshairLabelsPaint = null;
            }
            else
            {
                if (_rectangularSection != null && axes.MinLimit <= scaledPoint.X && scaledPoint.X <= axes.MaxLimit && Math.Abs((double)(scaledPoint.X - _rectangularSection.Xj)) >= 0.005)
                {
                    _rectangularSection.Xi = _rectangularSection.Xj = scaledPoint.X;
                }
            }
        }
    
        [RelayCommand]
        private void Released(LiveChartsCore.Kernel.Events.PointerCommandArgs pointerCommandArgs)
        {
            if (_rectangularSection != null)
            {
                var axes = XAxes.First();
                axes.CrosshairPaint = null;
                axes.CrosshairLabelsPaint = null;
            }
        }
    
Built with Hugo
Theme Stack designed by Jimmy