WPF工具箱图-在Series.Clear()之后的NullReferenceException

本文关键字:之后 NullReferenceException Clear Series 工具箱 WPF | 更新日期: 2023-09-27 18:03:26

错误发生在更复杂的上下文中,但可以在这个简单的示例中重现:

MainWindow.xaml

<Window>
  <StackPanel>
    <Button Click="Button_Click_1">Clear</Button>
    <Button Click="Button_Click_2">Modify</Button>
    <charting:Chart x:Name="chart" />
  </StackPanel>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    Random rand = new Random();
    ObservableCollection<KeyValuePair<double, double>> values =
        new ObservableCollection<KeyValuePair<double, double>>();
    public MainWindow()
    {
        InitializeComponent();
        values.Add(new KeyValuePair<double, double>(10, 10));
        values.Add(new KeyValuePair<double, double>(20, 40));
        values.Add(new KeyValuePair<double, double>(30, 90));
        values.Add(new KeyValuePair<double, double>(40, 160));
        values.Add(new KeyValuePair<double, double>(50, 250));
        AddSeries();
    }
    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        chart.Series.Clear();
        AddSeries();
    }
    private void AddSeries()
    {
        var series = new LineSeries();
        series.SetBinding(LineSeries.ItemsSourceProperty, new Binding());
        series.DataContext = values;
        series.DependentValueBinding = new Binding("Value");
        series.IndependentValueBinding = new Binding("Key");
        chart.Series.Add(series);
    }
    private void Button_Click_2(object sender, RoutedEventArgs e)
    {
        values[3] = new KeyValuePair<double,double>(40, rand.NextDouble() * 300);
    }
}

点击清除,然后点击修改。清除从图表中删除该系列并创建一个新系列。Modify修改系列绑定的源。删除的系列调用UpdateDataPoint,我得到一个NullReferenceException: ActualDependentRangeAxis是空的:

protected override void UpdateDataPoint(DataPoint dataPoint)
{
  double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(
    ActualDependentRangeAxis.Range.Maximum).Value;

我使用数据可视化开发版本4.0

WPF工具箱图-在Series.Clear()之后的NullReferenceException

我想我已经找到这个错误的原因了。在删除它们之前,您应该从每个系列中删除DataContext:

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    foreach (var series in chart.Series.OfType<Series>())
    {
        series.DataContext = null;
    }
    chart.Series.Clear();
    AddSeries();
}

如果您清除DataContext -事件将取消订阅,因为它应该。

编辑

在一种情况下,如果你快速点击修改按钮,然后立即点击清除按钮,它会崩溃。这是因为图表需要一些时间从事件中取消订阅数据点并隐藏它们。

无论如何,你可以手动取消订阅,但你必须使用反射,因为必要的方法(DetachEventHandlersFromDataPointsPlotArea)是内部的或私有的。

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    this.DetachAllEventsFromSeries();
    chart.Series.Clear();
    AddSeries();
}
private void DetachAllEventsFromSeries()
{
    var plotAreaProperty = typeof(DataPointSeries).GetProperty("PlotArea", BindingFlags.Instance | BindingFlags.NonPublic);
    var detachMethod = typeof(DataPointSeries).GetMethod("DetachEventHandlersFromDataPoints", BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (var series in chart.Series.OfType<DataPointSeries>().ToList())
    {
        var plotArea = (Panel)plotAreaProperty.GetValue(series, null);
        if (plotArea == null)
        {
            continue;
        }
        var datapoints = plotArea.Children.OfType<DataPoint>().ToList();
        detachMethod.Invoke(series, new[] { datapoints });
    }
}

此外,如果您有可能重新编译工具包库,您可以将此方法添加到DataPointSeries类中,而不需要反射,然后它将在没有开销的情况下执行。