"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>
我正在尝试使用图表工具包创建散点图系列,其中点的颜色由这些坐标处的条目数定义。基本上,我有一个带有 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>