简体   繁体   中英

How To Add Data Points Fast To A WPF Chart

Hello I have a big database from where I take like 1500 values in order to show in a chart.

My issues is that it takes a long time to display all the points, I think is because of animation settings.

How can I change the speed of the animation or is there a way to display the points faster?

<UserControl x:Class="Ipte.UI.Pages.StatisticsPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:gcl="clr-namespace:GuiControlLibrary;assembly=GuiControlLibrary"
        xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
             xmlns:time="clr-namespace:Ipte.UI"
        xmlns:chartToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
        mc:Ignorable="d"
        Height="800" Width="1200">
    <UserControl.Resources>
        <Style x:Key="Scater" TargetType="chartToolkit:ScatterDataPoint">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="chartToolkit:ScatterDataPoint">
                        <Viewbox x:Name="viewbox">
                            <!--<Ellipse Width="1" Height="1" Fill="Black"/>-->
                        </Viewbox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Width" Value="4"/>
            <Setter Property="Height" Value="4"/>
        </Style>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid x:Name="filterGrid" Grid.Column="0" Margin="4">
            <StackPanel>
                <TextBlock Text="" Margin="2"/>
                <toolkit:DatePicker x:Name="dpStartDate" Margin="2" />
                <time:TimePicker x:Name="tpStartTime" Margin="2"/>             
                <TextBlock Text="End date &amp; time:" Margin="2"/>
                <toolkit:DatePicker x:Name="dpEndDate" Margin="2"/>
                <time:TimePicker x:Name="tpEndTime" Margin="2"/>

                <gcl:GuiGroupBox Header="Select router" BorderBrush="LightGray">
                    <UniformGrid Rows="2" Columns="2">
                        <CheckBox x:Name="cbEr11" Content="ER 1.1" Margin="2"/>
                        <CheckBox x:Name="cbEr12" Content="ER 1.2" Margin="2"/>
                        <CheckBox x:Name="cbEr21" Content="ER 2.1" Margin="2"/>
                        <CheckBox x:Name="cbEr22" Content="ER 2.1" Margin="2"/>
                    </UniformGrid>
                </gcl:GuiGroupBox>
                <TextBlock Text="" Margin="2"/>
                <ComboBox x:Name="cmbGoodBad" Margin="2"/>
                <TextBlock Text="" Margin="2"/>
                <TextBox x:Name="" Margin="2"/>
                <TextBlock Text="" Margin="2"/>
                <TextBox x:Name="" Margin="2"/>
                <gcl:GuiGroupBox Header="Select value" BorderBrush="LightGray">
                    <StackPanel>
                        <RadioButton x:Name="combValueA" Content="Value A" Margin="2"/>
                        <RadioButton x:Name="combValueB" Content="Value B" Margin="2"/>
                        <RadioButton x:Name="combValueC" Content="Value C" Margin="2"/>
                    </StackPanel>
                </gcl:GuiGroupBox>
                <Button x:Name="btnResetFilters" Content="Reset filters" Margin="2 10 2 2" Click="ResetFilters_Click"/>
                <Button x:Name="btnUpdateChart" Content="Update Chart" Margin="2 2 2 2" Click="UpdateChartAndFilters_Click"/>
                <Button x:Name="btnLoadFile" Content="Load file..." Grid.Column="0" VerticalAlignment="Top" Margin="2" Visibility="Visible" Click="OpenFile_Click"/>
            </StackPanel>
            <Button x:Name="deleteDatabase" Content="Delete database" Grid.Column="0" VerticalAlignment="Bottom" Margin="2" Click="deleteDatabase_Click"/>
        </Grid>
        <chartToolkit:Chart Grid.Column="1" x:Name="dataChart">
            <chartToolkit:Chart.Series>
                <chartToolkit:ScatterSeries x:Name="scatterSeries"
                                        ItemsSource="{Binding}"
                                        DependentValueBinding="{Binding Path=Value}"
                                        IndependentValueBinding="{Binding Path=Key}" 
                                        IsSelectionEnabled="False"
                                           AnimationSequence="Simultaneous">
                    <chartToolkit:ScatterSeries.IndependentAxis>
                        <chartToolkit:DateTimeAxis Orientation="X" Title="Time"/>
                    </chartToolkit:ScatterSeries.IndependentAxis>
                    <chartToolkit:ScatterSeries.DependentRangeAxis>
                        <chartToolkit:LinearAxis Orientation="Y" Title="Points" x:Name="yAxis"/>
                    </chartToolkit:ScatterSeries.DependentRangeAxis>
                </chartToolkit:ScatterSeries>
                <chartToolkit:LineSeries x:Name="lineSeriesMax"
                                         Title="Maximum"
                                         ItemsSource="{Binding}"
                                         DependentValueBinding="{Binding Path=Value}"
                                         IndependentValueBinding="{Binding Path=Key}">
                </chartToolkit:LineSeries>
                <chartToolkit:LineSeries x:Name="lineSeriesMin"
                                         Title="Minimum"
                                         ItemsSource="{Binding}"
                                         DependentValueBinding="{Binding Path=Value}"
                                         IndependentValueBinding="{Binding Path=Key}">
                </chartToolkit:LineSeries>
                <chartToolkit:LineSeries x:Name="lineSeriesAvg"
                                         Title="Average"
                                         ItemsSource="{Binding}"
                                         DependentValueBinding="{Binding Path=Value}"
                                         IndependentValueBinding="{Binding Path=Key}">
                </chartToolkit:LineSeries>
            </chartToolkit:Chart.Series>
        </chartToolkit:Chart>
    </Grid>
</UserControl>

This is the way my points are displayed:

制图

I know this is an older question but I wanted to share my thoughts on charting in WPF that goes beyond just plotting a few bars or scatter points or lines.

Probably everyone agrees that the stock WPF library was not built and intended to handle many thousands or even millions of datapoints when it comes to charting. Whatever workarounds I tried such as data sampling, I was never really satisfied with stock WPF charting capabilities. If you are working to chart data other than just as a one-time prototyping pursuit I highly recommend you to look at a professional WPF charting library.

I have in particular used Scichart , a third party vendor WPF library, for many years and cannot speak highly enough of its capabilities. I often chart hundreds of thousands, sometimes millions of scatter data points and most recently also rendered 3D charts with tons of data and find Scichart highly performant. It costs more than "free" but I find the investment more than worth it because (and I try to incorporate your questions here):

  • The 2D and 3D libraries are rock solid, what I mean with that is that rendering performance is stellar, bindings just work, virtually anything can be customized, full MVVM support

  • The documentation and support forum are probably the best part of what Scichart has to offer. Most questions, even trickier ones are addressed already and if something can't be found then other users or the support team respond in a timely fashion.

  • In particular to your question, with Scichart you simply add the dataset all at once as array via binding or directly and it renders within milliseconds. Its real-time capabilities are also amazing if you need to add data points one by one.

I strongly suggest you give them a try, they offer a trial license. I tried charting with DevExpress, of which I also own a license, and their charting capabilities top out beyond a few datapoints and their charts are imho better suited for dashboards with few data points. I also tried and used Arction's lightningchart library for a while and while raw performance was on par with Scichart, their styling is absolutely horrific, their MVVM capabilities are all but non existent, in fact when you take a look at their documentation you see that a lot of solution suggestions to problems are presented in code behind. Their support forum and Q&A repository is also very sparsely populated, something that usually really turns me off because at some point every developer will encounter issues he/she needs to look up for a solution.

To be honest, if you only look to chart 1500 data points and have a DevExpress license then by all means, use them, because I think they can still handle 1500 points, though more might become tricky. But if you ever see the need for charting capabilities of larger data sets then I can't speak of Scichart highly enough. Why? Because I worked with that library since early version 1.3.x.xxxx. I truly believe they are the best charting library out there in WPF space.

What contributes the most to slow down your chart is all the events generated to draw your series point-by-point as they are added to the view model collection. Adding them all at once solves that problem:

在此处输入图像描述

Extend ObservableCollection to support AddRange , as shown HERE :

public class ObservableCollectionRange<T> : ObservableCollection<T>
{
    public void AddRange(IEnumerable<T> items)
    {
        this.CheckReentrancy();
        foreach (var item in items)
            this.Items.Add(item);
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

And then use it in your view model:

public class MyViewModel
{
    public ObservableCollectionRange<KeyValuePair<double, double>> Power { get; set; }
    public ObservableCollectionRange<KeyValuePair<double, double>> PowerAvg { get; set; }

    public MyViewModel()
    {
        Power = new ObservableCollectionRange<KeyValuePair<double, double>>();
        PowerAvg = new ObservableCollectionRange<KeyValuePair<double, double>>();
    }

    public void Add(double x, double y)
    {
        Power.Add(new KeyValuePair<double, double>(x, y));
        
        double xmin = Power.Min(kvp => kvp.Key);
        double xmax = Power.Max(kvp => kvp.Key);

        double ymin = Power.Min(kvp => kvp.Value);
        double ymax = Power.Max(kvp => kvp.Value);
        double yavg = Power.Average(kvp => kvp.Value);

        PowerAvg.Clear(); 
        PowerAvg.Add(new KeyValuePair<double, double>(xmin, yavg));
        PowerAvg.Add(new KeyValuePair<double, double>(xmax, yavg));
    }

    public void AddRange(IEnumerable<KeyValuePair<double, double>> items)
    {
        Power.AddRange(items);

        double xmin = Power.Min(kvp => kvp.Key);
        double xmax = Power.Max(kvp => kvp.Key);

        double ymin = Power.Min(kvp => kvp.Value);
        double ymax = Power.Max(kvp => kvp.Value);
        double yavg = Power.Average(kvp => kvp.Value);

        PowerAvg.Clear();
        PowerAvg.Add(new KeyValuePair<double, double>(xmin, yavg));
        PowerAvg.Add(new KeyValuePair<double, double>(xmax, yavg));
    }
}

And at button click event:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ShowPoints();
    }

    private void ShowPoints()
    {
        Random random = new Random();
        ObservableCollection<KeyValuePair<double, double>> oc = new ObservableCollection<KeyValuePair<double, double>>();

        for (int i = 1; i <= 1500; i++)
            oc.Add(new KeyValuePair<double, double>(i, random.NextDouble()));

        vm.AddRange(oc);
    }

If your main priority is Line charts and you consider switching to a third-party component vendor, you may consider DevExpress ChartControl as a suitable option.

For instance, the latest version of DevExpress components ships with the "Large Datasource" demo module, where up to 500K points can be shown without major performance degradation (this includes interactive operations such as scrolling and zooming).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM