You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现LiveCharts多图表LabelVisual随光标同步更新?

多图表同步更新LabelVisual文本的解决方案

问题场景

我有多个图表,每个图表都包含一个LabelVisual,要求当光标沿X轴移动时,所有图表的该标签文本均需更新,而非仅更新光标所在的图表。目前仅能实现光标所在图表的标签文本更新,现寻求解决方案。

相关代码

MainWindow.axaml

<Window>
    <Design.DataContext>
        <vm:MainWindowViewModel2/>
    </Design.DataContext>
    <Window.DataContext>
        <vm:MainWindowViewModel2 />
    </Window.DataContext>
    <ScrollViewer AllowAutoHide="True">
            <ItemsControl x:Name="ItemsGraphs" PointerMoved="ItemsControl_PointerMoved" ItemsSource="{Binding ItemsGraphs}" >
                <ItemsControl.DataTemplates>
                    <views:ItemsTemplateSelector>
                        <DataTemplate x:Key="{x:Type vm:ViewModelLineSmoothness}">
                            <views:LineSmoothness/>
                        </DataTemplate>
                        <DataTemplate x:Key="{x:Type vm:ViewModelDiscreteSignal}">
                            <views:DiscreteSignal/>
                        </DataTemplate>
                    </views:ItemsTemplateSelector>
                </ItemsControl.DataTemplates>
            </ItemsControl>
    </ScrollViewer>
</Window>

LineSmoothness.axaml

<UserControl>
    <lvca:CartesianChart Name="ChartLine"
                        Series="{Binding Series}"
                        Sections="{Binding Sections}"
                        YAxes="{Binding YAxes}"
                        XAxes="{Binding XAxes}"
                        Background="White"
                        Height="200"
                        Margin="5"
                        LegendPosition="Right"
                        LegendTextPaint="{Binding LegendTextPaint}"
                        LegendTextSize="12"
                        TooltipTextSize="10"
                        VisualElements="{Binding VisualElements}"
                        PointerPressedCommand="{Binding PointerDownCommand}"
                        PointerMoveCommand="{Binding PointerMoveCommand}"
                        TooltipPosition ="Auto"
                        ZoomMode="ZoomY">
        <lvca:CartesianChart.DrawMarginFrame>
            <lvc:DrawMarginFrame>
                <lvc:DrawMarginFrame.Stroke>
                    <lvcp:SolidColorPaint Color="#808080" StrokeThickness="1" />
                </lvc:DrawMarginFrame.Stroke>
            </lvc:DrawMarginFrame>
        </lvca:CartesianChart.DrawMarginFrame>
    </lvca:CartesianChart>

</UserControl>

ViewModelLineSmoothness.cs

...
        public LiveChartsCore.SkiaSharpView.VisualElements.LabelVisual labelMarker;

        public ObservableCollection<ChartElement<SkiaSharpDrawingContext>> VisualElements  => visuals;

        public ViewModelLineSmoothness()
        {
            labelMarker = new LiveChartsCore.SkiaSharpView.VisualElements.LabelVisual
            {
                HorizontalAlignment = Align.Start,
                TextSize = 15,
                X = 100,
                Y = 100,
                Paint = new SolidColorPaint(SKColors.Red)
                {
                    ZIndex = 11,
                    SKTypeface = SKTypeface.FromFamilyName("Courier New")
                },
                BackgroundColor = new LvcColor(55, 71, 79).WithOpacity(10),
                Padding = new Padding(5),
                LocationUnit = MeasureUnit.Pixels,
            };
            visuals.Add(labelMarker);
        }
        [RelayCommand]
        public void PointerMoveCommand(PointerCommandArgs args)
        {
           labelMarker.Text = $"{args.PointerPosition.X:f2}";
           labelMarker.X = args.PointerPosition.X;
           labelMarker.Y = args.PointerPosition.Y;
           labelMarker.Invalidate((Chart<SkiaSharpDrawingContext>)args.Chart.CoreChart);
        }
...

解决方案

核心思路是将光标X轴位置作为共享状态托管在主ViewModel中,所有子图表ViewModel监听该状态变化,实现同步更新。

步骤1:主ViewModel添加共享光标位置属性

修改MainWindowViewModel2,维护可观察的光标X轴位置,并在值变更时通知所有子ViewModel更新标签:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;

public class MainWindowViewModel2 : INotifyPropertyChanged
{
    private double _currentCursorX;
    public double CurrentCursorX
    {
        get => _currentCursorX;
        set
        {
            if (_currentCursorX != value)
            {
                _currentCursorX = value;
                OnPropertyChanged();
                // 遍历所有子ViewModel,触发标签更新
                foreach (var item in ItemsGraphs)
                {
                    if (item is ViewModelLineSmoothness lineVm)
                        lineVm.UpdateLabelMarker(value);
                    else if (item is ViewModelDiscreteSignal discreteVm)
                        discreteVm.UpdateLabelMarker(value);
                }
            }
        }
    }

    public ObservableCollection<object> ItemsGraphs { get; set; } = new();

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:子ViewModel添加标签更新方法

修改ViewModelLineSmoothness,移除原有的PointerMoveCommand,新增接收共享X轴位置并更新标签的方法:

public class ViewModelLineSmoothness
{
    public LiveChartsCore.SkiaSharpView.VisualElements.LabelVisual labelMarker;
    public ObservableCollection<ChartElement<SkiaSharpDrawingContext>> VisualElements => visuals;
    private readonly ObservableCollection<ChartElement<SkiaSharpDrawingContext>> visuals = new();

    public ViewModelLineSmoothness()
    {
        labelMarker = new LiveChartsCore.SkiaSharpView.VisualElements.LabelVisual
        {
            HorizontalAlignment = Align.Start,
            TextSize = 15,
            X = 100,
            Y = 100,
            Paint = new SolidColorPaint(SKColors.Red)
            {
                ZIndex = 11,
                SKTypeface = SKTypeface.FromFamilyName("Courier New")
            },
            BackgroundColor = new LvcColor(55, 71, 79).WithOpacity(10),
            Padding = new Padding(5),
            LocationUnit = MeasureUnit.Pixels,
        };
        visuals.Add(labelMarker);
    }

    // 接收主ViewModel传递的X轴值,更新标签
    public void UpdateLabelMarker(double cursorX)
    {
        labelMarker.Text = $"{cursorX:f2}";
        // 如果需要同步标签的X坐标,可根据图表坐标逻辑转换后赋值
        // labelMarker.X = ...;
        labelMarker.Invalidate();
    }
}

步骤3:主窗口统一处理光标移动事件

在MainWindow.axaml.cs中实现ItemsControl_PointerMoved,将屏幕坐标转换为图表X轴值,更新主ViewModel的共享状态:

using Avalonia.Input;
using LiveChartsCore.SkiaSharpView.Avalonia;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ItemsControl_PointerMoved(object? sender, PointerEventArgs e)
    {
        if (DataContext is not MainWindowViewModel2 mainVm) return;
        
        var itemsControl = sender as ItemsControl;
        if (itemsControl?.Items.Count == 0) return;

        // 获取任意一个图表实例用于坐标转换
        var firstContainer = itemsControl.ItemContainerGenerator.ContainerFromIndex(0) as ContentPresenter;
        if (firstContainer?.ContentTemplate.LoadContent() is not LineSmoothness lineView) return;
        
        var chart = lineView.ChartLine;
        if (chart == null) return;

        // 转换屏幕坐标为图表X轴数据值
        var pointerPos = e.GetPosition(chart);
        var dataPoint = chart.CoreChart.PointerPositionToDataPoint(pointerPos);
        if (dataPoint == null) return;

        mainVm.CurrentCursorX = dataPoint.X;
    }
}

步骤4:移除子图表的光标移动命令绑定

修改LineSmoothness.axaml,删除PointerMoveCommand的绑定:

<lvca:CartesianChart Name="ChartLine"
                    Series="{Binding Series}"
                    Sections="{Binding Sections}"
                    YAxes="{Binding YAxes}"
                    XAxes="{Binding XAxes}"
                    Background="White"
                    Height="200"
                    Margin="5"
                    LegendPosition="Right"
                    LegendTextPaint="{Binding LegendTextPaint}"
                    LegendTextSize="12"
                    TooltipTextSize="10"
                    VisualElements="{Binding VisualElements}"
                    PointerPressedCommand="{Binding PointerDownCommand}"
                    TooltipPosition ="Auto"
                    ZoomMode="ZoomY">
    <!-- 其他内容不变 -->
</lvca:CartesianChart>

内容的提问来源于stack exchange,提问作者chemo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:59:53