"No suitable axis is available for plotting the dependent value" 将 MVVM 与多个词典结合使用

"No suitable axis is available for plotting the dependent value" using MVVM with multiple dictionaries

我正在尝试使用图表工具包创建散点图系列,其中点的颜色由这些坐标处的条目数定义。基本上,我有一个带有 Count(int) 属性 的 GraphPointViewModel class 和一个画笔颜色 属性,效果很好。

在定义散点图系列的 ViewModel 中,我有以下 属性:

public ObservableDictionary<int, ObservableDictionary<int, GraphPointViewModel>> Data { get; set; }

这个 ObservableDictionary 只是将 INotifyPropertyChanged 方法添加到 Dictionary class,您注意到 Dictionary 有另一个 Dictionary。原因是base Dictionary 的Key 是X 轴值,而child Dictionary 的key 是Y 轴值。然后使用 GraphPointViewModel 定义系列的样式(即根据要求为点着色)。

这个系列的填充被证明工作正常。问题来自绑定到 Y 轴。

我的图表来自 WPF 图表工具包 (System.Windows.Controls.DataVisualization),在 XAML 中定义为:

        <chartingToolkit:Chart Grid.RowSpan="2" Name="GrapherChart">

        <chartingToolkit:Chart.Axes>
            <chartingToolkit:LinearAxis Orientation="X" Interval="50" Minimum="{Binding Xmin}" Maximum="{Binding Xmax}" FontSize="10" Title="Phase"/>
            <chartingToolkit:LinearAxis Orientation="Y" Interval="500" Minimum="{Binding Ymin}" Maximum="{Binding Ymax}" FontSize="10" ShowGridLines="True" Title="Amplitude"/>
        </chartingToolkit:Chart.Axes>

        <chartingToolkit:ScatterSeries
            DependentValuePath="Value.Key"
            IndependentValuePath="Key"
            ItemsSource="{Binding Data}">
            <chartingToolkit:ScatterSeries.DataPointStyle>
                <Style TargetType="chartingToolkit:ScatterDataPoint">
                    <Setter Property="Background" Value="{Binding Value.Value.Colour}"></Setter>
                </Style>
            </chartingToolkit:ScatterSeries.DataPointStyle>
        </chartingToolkit:ScatterSeries>

        <chartingToolkit:Chart.LegendStyle>
            <Style TargetType="Control">
                <Setter Property="Width" Value="0"/>
                <Setter Property="Height" Value="0"/>
            </Style>
        </chartingToolkit:Chart.LegendStyle>
    </chartingToolkit:Chart>

当我 运行 我的应用程序时,即使没有添加任何数据,我也会收到错误 "No suitable axis is available for plotting the dependent value"。我曾尝试将 DependentValuePath 更改为 Key,但这只是使用与 X 轴相同的 Key。怎样绑定字典才能达到我需要的效果?

更新 已被要求发布 ViewModel classes(尽管我认为他们不会提供帮助),这是 GraphPointViewModel:

public class GraphPointViewModel : Mvvm.ViewModel
{

    private int _count;

    public int Count
    {
        get { return _count; }
        set { SetPropertyAndNotify(ref _count, value, new []{"Colour"}); }
    }

    public Brush Colour
    {
        get
        {
            return Count >= 5 ?
                Brushes.Black :
                Brushes.Gray;
        }
    }
}

这是负责整理图形数据的 ViewModel:

    public class EventGrapherViewModel : DataHandlingViewModel
{
    private double _xmin;

    public double Xmin
    {
        get { return _xmin; }
        set { SetPropertyAndNotify(ref _xmin, value); }
    }

    private double _ymin = -2500;

    public double Ymin
    {
        get { return _ymin; }
        set { SetPropertyAndNotify(ref _ymin, value); }
    }

    private double _zmin;

    public double Zmin
    {
        get { return _zmin; }
        set { SetPropertyAndNotify(ref _zmin, value); }
    }

    private double _xmax = 400;

    public double Xmax
    {
        get { return _xmax; }
        set { SetPropertyAndNotify(ref _xmax, value); }
    }

    private double _ymax = 2500;

    public double Ymax
    {
        get { return _ymax; }
        set { SetPropertyAndNotify(ref _ymax, value); }
    }

    private double _zmax;

    public double Zmax
    {
        get { return _zmax; }
        set { SetPropertyAndNotify(ref _zmax, value); }
    }

    public ObservableDictionary<int, ObservableDictionary<int, GraphPointViewModel>> Data { get; set; }

    /// <summary>
    /// The current channel being graphed. 
    /// Note that less than or equal to 0 means no event data is being graphed.
    /// </summary>
    private int _currentChannel;

    public EventGrapherViewModel()
    {
        var syncLock = new object();

        Data = new ObservableDictionary<int, ObservableDictionary<int, GraphPointViewModel>>();

        // EnableCollectionSynchronization allows updating of the UI when the collection is changed from a background thread.
        BindingOperations.EnableCollectionSynchronization(Data, syncLock);
    }

    public void AddDataPoint(int angle, int amplitude)
    {
        try
        {
            if (!Data.ContainsKey(angle))
            {
                Data.Add(angle, new ObservableDictionary<int, GraphPointViewModel>());
            }

            var matchingAngle = Data[angle];

            if (!matchingAngle.ContainsKey(amplitude))
            {
                matchingAngle.Add(amplitude, new GraphPointViewModel());
            }

            var matchingAmplitudeViewModel = matchingAngle.First(ma => ma.Key == amplitude).Value;
            matchingAmplitudeViewModel.Count++;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }

    protected override void GrapherSelectionChanged(GrapherSelection grapherSelection)
    {
        Data.Clear();
        _currentChannel = (int)grapherSelection;
    }

    public override void AddDataPoint(Data dataPoint)
    {
        if (_currentChannel <= 0)
            return;

        var eventData = dataPoint as EventData;
        if (eventData == null)
            return;

        foreach (var eventDataPoint in eventData.EventDataPoints)
        {
            AddDataPoint(eventDataPoint.PhaseAngle, eventDataPoint.AmplitudesByChannel[_currentChannel]);
        }
    }

数据通过 MQTT 接收,然后使用 Broker 发布,因此 ViewModel 直接订阅了它。

我已经通过删除两个词典解决了这个问题。我现在有一个 EventDataPointsViewModel class:

public class EventDataPointsViewModel : Mvvm.ViewModel
{

    private int _phaseAngle;

    public int PhaseAngle
    {
        get { return _phaseAngle; }
        set { SetPropertyAndNotify(ref _phaseAngle, value); }
    }

    private int _amplitude;

    public int Amplitude
    {
        get { return _amplitude; }
        set { SetPropertyAndNotify(ref _amplitude, value); }
    }

    private int _count;

    public int Count
    {
        get { return _count; }
        set { SetPropertyAndNotify(ref _count, value, new[] { "Colour" }); }
    }

    public Brush Colour
    {
        get
        {
            return Count >= 5 ?
                Brushes.Black :
                Brushes.Gray;
        }
    }
}

现在数据

public ObservableCollection<EventDataPointsViewModel> Data { get; set; }

而 AddDataPoint 方法是

    public void AddDataPoint(int angle, int amplitude)
    {
        try
        {
            var dataPoint = Data.FirstOrDefault(d => d.PhaseAngle == angle && d.Amplitude == amplitude);
            if (dataPoint != null)
            {
                dataPoint.Count++;
            }
            else
            {
                Data.Add(new EventDataPointsViewModel { Amplitude = amplitude, PhaseAngle = angle, Count = 1 });
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }

图表的XAML是

    <chartingToolkit:Chart Grid.RowSpan="2" Name="GrapherChart">

        <chartingToolkit:Chart.Axes>
            <chartingToolkit:LinearAxis Orientation="X" Interval="50" Minimum="{Binding Xmin}" Maximum="{Binding Xmax}" FontSize="10" Title="Phase"/>
            <chartingToolkit:LinearAxis Orientation="Y" Interval="500" Minimum="{Binding Ymin}" Maximum="{Binding Ymax}" FontSize="10" ShowGridLines="True" Title="Amplitude"/>
        </chartingToolkit:Chart.Axes>

        <chartingToolkit:ScatterSeries
            DependentValuePath="Amplitude"
            IndependentValuePath="PhaseAngle"
            ItemsSource="{Binding Data}">
            <chartingToolkit:ScatterSeries.DataPointStyle>
                <Style TargetType="chartingToolkit:ScatterDataPoint">
                    <Setter Property="Background" Value="{Binding Colour}"></Setter>
                </Style>
            </chartingToolkit:ScatterSeries.DataPointStyle>
        </chartingToolkit:ScatterSeries>

        <chartingToolkit:Chart.LegendStyle>
            <Style TargetType="Control">
                <Setter Property="Width" Value="0"/>
                <Setter Property="Height" Value="0"/>
            </Style>
        </chartingToolkit:Chart.LegendStyle>
    </chartingToolkit:Chart>