How to find the right code to create a line chart

Hobby02 26 Reputation points

In a Visual Studio 2019 C# WPF (.NET) project I like to add a line chart to visualise the measurement data of an amplifier as function of frequency.
In this project I am controlling a frequency generator, making a frequency sweep (X-Axis).
The voltage of the generator is the input of an amplifier under test.
The outputvoltage of the amplifier is measured (Y-axis) as function of the frequency.
I have meanwhile seen a lot of different codes on a lot of sites on the internet how to create a chart and come to the conlusion that there is missing some manual
which is describing exactly how you have to write the code to create a simple chart, explaining the properties of the chart and how to write their codes .
I like to find a website which describes these code and how to type them.
I found a Microsoft website how to create a basic chart step by step using the propertypanel. But I like to know the underlying code.
For example how do I find that to change the background color of the chart should be like Chartareas[0].Backcolor=Color.Blue;
or ChartAreas[0].AxisY.Enabled = AxisEnabled.True; and how do I have to write the code to get the measurement data (e.g. the frequency array and the outputvoltage array) in the chart?
I hope you may help me a bit further.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,667 questions
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,198 questions
{count} votes

5 answers

Sort by: Most helpful
  1. DaisyTian-1203 11,616 Reputation points

    I will show you a line chart with tracking cpu usage for you to refer.
    Xaml code:

            <local:PolygonConverter x:Key="PolygonConverter"/>  
                <RowDefinition Height="Auto"/>  
                <TextBlock Margin="5" Text="CPU:"/>  
                <TextBlock Margin="0, 5" Text="{Binding LastCpuValue, StringFormat=##0.##}" FontWeight="Bold"/>  
                <TextBlock Margin="0, 5" Text="%" FontWeight="Bold"/>  
            <Border Margin="5" Grid.Row="1" BorderThickness="1" BorderBrush="Red" SnapsToDevicePixels="True">  
                <Canvas ClipToBounds="True">  
                    <Polygon Stroke="LightBlue" Fill="AliceBlue">  
                            <Style TargetType="Polygon">  
                                <Setter Property="Points">  
                                        <MultiBinding Converter="{StaticResource PolygonConverter}">  
                                            <Binding Path="ProcessorTime.Values"/>  
                                            <Binding Path="ActualWidth" RelativeSource="{RelativeSource AncestorType=Canvas}"/>  
                                            <Binding Path="ActualHeight" RelativeSource="{RelativeSource AncestorType=Canvas}"/>  

    C# code:

     public class MainViewModel : NotifyPropertyChanged  
            public RoundRobinCollection ProcessorTime { get; }  
            private float _lastCpuValue;  
            public float LastCpuValue  
                get => _lastCpuValue;  
                    _lastCpuValue = value;  
            private async void ReadCpu()  
                    using (PerformanceCounter cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"))  
                        while (true)  
                            LastCpuValue = cpuCounter.NextValue();  
                            await Task.Delay(1000);  
                catch (Exception ex)  
            public MainViewModel()  
                ProcessorTime = new RoundRobinCollection(100);  
        public class RoundRobinCollection : NotifyPropertyChanged  
            private readonly List<float> _values;  
            public IReadOnlyList<float> Values => _values;  
            public RoundRobinCollection(int amount)  
                _values = new List<float>();  
                for (int i = 0; i < amount; i++)  
            public void Push(float value)  
    public class NotifyPropertyChanged : INotifyPropertyChanged  
            public event PropertyChangedEventHandler PropertyChanged;  
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)  
                => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
     public class PolygonConverter : IMultiValueConverter  
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)  
                PointCollection points = new PointCollection();  
                if (values.Length == 3 && values[0] is IReadOnlyList<float> dataPoints && values[1] is double width && values[2] is double height)  
                    points.Add(new Point(0, height));  
                    points.Add(new Point(width, height));  
                    double step = width / (dataPoints.Count - 1);  
                    double position = width;  
                    for (int i = dataPoints.Count - 1; i >= 0; i--)  
                        points.Add(new Point(position, height - height * dataPoints[i] / 100));  
                        position -= step;  
                return points;  
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => null;  

    The result picture :

    Did my answer give you any help? If it doesn't, could you show me the link of your Microsoft website how to create a basic chart step by step using the propertypanel for me to analyze ?

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    2 people found this answer helpful.

  2. Peter Fleischer (former MVP) 19,231 Reputation points

    you can write yours own plot element like in following demo:


    <Window x:Class="WpfApp1.Window051"  
            Title="Window051" Height="400" Width="400">  
        <cc:Plot Margin="10" Data="{Binding Points}"/>  

    And code:

    using System;  
    using System.ComponentModel;  
    using System.Runtime.CompilerServices;  
    using System.Threading.Tasks;  
    using System.Windows;  
    using System.Windows.Controls;  
    using System.Windows.Media;  
    using System.Windows.Shapes;  
    namespace WpfApp051  
      public class ViewModel : INotifyPropertyChanged  
        public ViewModel() => GeneratePoints();  
        private async void GeneratePoints()  
          Random rnd = new Random();  
            double[] db = new double[100];  
            for (int i = 0; i < Points.GetUpperBound(0); i++) db[i] = Points[i+1];  
            db[99] = rnd.Next(20, 200);  
            Points = db;  
            await Task.Delay(50);  
          } while (true);  
        public double[] Points { get; set; } = new double[100];  
        public event PropertyChangedEventHandler PropertyChanged;  
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
      class Plot : FrameworkElement  
        VisualCollection children;  
        Canvas plotArea;  
        Polyline poly;  
        Line xAxis, yAxis;  
        Point Origin = new Point(10, 10);  
        double[] Points { get; set; } = new double[0];  
        public Plot()  
          children = new VisualCollection(this);  
        public int XPoints { get; set; } = 100;  
        public int YPoints { get; set; } = 100;  
        public Brush LineColor { get; set; } = Brushes.Red;  
        public static readonly DependencyProperty DataProperty  
        = DependencyProperty.RegisterAttached("Data", typeof(double[]), typeof(Plot),  
          new FrameworkPropertyMetadata(null,FrameworkPropertyMetadataOptions.AffectsArrange,   
            new PropertyChangedCallback(DataChanged)));  
        public static double[] GetData(DependencyObject obj) => (double[])obj.GetValue(DataProperty);  
        public static void SetData(DependencyObject obj, double[] data) => obj.SetValue(DataProperty, data);  
        private static void DataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)  
          var plot = d as Plot;  
          if (plot == null) throw new InvalidOperationException("Error using Data property.");  
          var data = e.NewValue as double[];  
          plot.Points = data;  
        void initAxis()  
          xAxis = new Line() { Stroke = Brushes.LightBlue, StrokeThickness = 2 };  
          yAxis = new Line() { Stroke = Brushes.LightBlue, StrokeThickness = 2 };  
        void initPlotArea()  
          plotArea = new Canvas()  
            LayoutTransform = new ScaleTransform() { ScaleY = -1 },  
            RenderTransform = new TransformGroup() { Children = { new ScaleTransform(), new TranslateTransform() } }  
          poly = new Polyline() { Stroke = LineColor, StrokeThickness = 1 };  
        void rearrangeAxis(Size size)  
          xAxis.X1 = 0;  
          xAxis.X2 = size.Width;  
          xAxis.Y1 = xAxis.Y2 = size.Height - Origin.Y;  
          yAxis.X1 = yAxis.X2 = Origin.X;  
          yAxis.Y1 = 0;  
          yAxis.Y2 = size.Height;  
          xAxis.Arrange(new Rect(xAxis.DesiredSize));  
          yAxis.Arrange(new Rect(yAxis.DesiredSize));  
        void rearrangePlotArea(Size size)  
          plotArea.Width = size.Width - Origin.X;  
          plotArea.Height = size.Height - Origin.Y;  
          var translate = (TranslateTransform)((TransformGroup)plotArea.RenderTransform).Children[1];  
          translate.X = Origin.X;  
          translate.Y = 0;  
          plotArea.Arrange(new Rect(plotArea.DesiredSize));  
          var points = new PointCollection();  
          double xStep = plotArea.Width / XPoints;  
          double yFactor = plotArea.Height / YPoints;  
          for (int i = 0; i < YPoints && i <= Points.GetUpperBound(0); i++)  
            points.Add(new Point(i * xStep, Points[i]));  
          poly.Points = points;  
        protected override Size ArrangeOverride(Size finalSize)  
          return finalSize;  
        protected override Visual GetVisualChild(int index) => children[index];  
        protected override int VisualChildrenCount => children.Count;  



    1 person found this answer helpful.

  3. Ken Tucker 5,846 Reputation points

  4. Hobby02 26 Reputation points

    Thank you for your message.
    It should have been fine if I had found these namespaces in your first message.

    Unfortunately "using System.Windows.Data;" and "using System.Windows.Media;"
    could not be found by the system.
    By searching on the internet I found that I had to add a Reference for these two namespaces.
    After adding those there are still errors:

    CS0012 The type 'Point' is defined in an assembly that is not referenced. You must add a reference to assembly 'WindowsBase, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35 and
    CS0012 The type 'Freezable' is defined in an assembly that is not referenced. You must add a reference to assembly 'WindowsBase, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35
    CS1503 Argument : cannot convert from 'double' to 'int'

    These three errors refer to the lines:

    points.Add(new Point(0, height));
    points.Add(new Point(width, height));
    points.Add(new Point(position, height - height * dataPoints[i] / 100));

    Sorry, but your example to explain/show me a simple line chart seems to be too complex for me.

  5. Hobby02 26 Reputation points

    Also your example gives a lot of errors after loading in VS2019 (also by adding the concerning References w.r.t. using System.Windows.Controls;
    using System.Windows.Media; and using System.Windows.Shapes;)

    I think it is worth to mention first how you did create your project after selecting Create New Project.
    Supposing a chart is created, I then obviously have to startup in C# - Desktop with a template Windows Form App (.NET Framework)
    If I delete first the generated 25 lines in the file Form1.cs and then paste your text (132 lines), then 103 errors are generated.

    I like to know how I can create (by code in C#) a line chart where I can show the result of the measurement results which are received by the Serial Input Port (in my case).
    And I like to write code that the X- axis is getting the Start-frequency as X-min of the Frequency Sweep and the Stop-frequency as X-max.
    The vertical axis is initially auto set by the values of the measurement results, but there has to be an option to manually set Y-min and Y-max, derived from textboxes
    to zoom in to the measurement data.

    I like to find a document about Chart Control explaining how one can write code in C# to get the desired line chart . And visualised how the Line Chart look like.
    One can set a lot of properties of a chart, but it seems also to be possible to write it in code.
    So in my case the program knows the start- and stop frequency values of the frequency sweep and assigns these values to X-min and X-max.
    The Y-min and Y max values are found by calculating the min- and max values of the incoming data from the Serial Input Port.
    So it is needed to initally write some lines of code to setup a Line Chart and then after the measurement the curve is drawn.
    That is what I'm looking for.