{"id":175,"date":"2013-04-17T06:25:17","date_gmt":"2013-04-17T06:25:17","guid":{"rendered":"http:\/\/mindfusion.eu\/blog\/?p=175"},"modified":"2021-01-08T15:13:35","modified_gmt":"2021-01-08T15:13:35","slug":"real-time-line-chart","status":"publish","type":"post","link":"https:\/\/mindfusion.dev\/blog\/real-time-line-chart\/","title":{"rendered":"Real Time Line Chart"},"content":{"rendered":"<p>In today\u2019s post we are going to build a real time line chart with the following features:<\/p>\n<ul>\n<li>8 data series with 100 points each.<\/li>\n<li>Ten times per second we add 10 random points to each series and remove the last ten.<\/li>\n<li>Each tenth of the second we update the min and max values of the X-axis.<\/li>\n<li>Legend<\/li>\n<\/ul>\n<div id=\"attachment_185\" style=\"width: 518px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/mindfusion.dev\/blog\/wp-content\/uploads\/2013\/04\/line_chart_performance.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-185\" class=\"size-full wp-image-185\" src=\"http:\/\/mindfusion.dev\/blog\/wp-content\/uploads\/2013\/04\/line_chart_performance.png\" alt=\"The final chart\" width=\"508\" height=\"298\" srcset=\"https:\/\/mindfusion.dev\/blog\/wp-content\/uploads\/2013\/04\/line_chart_performance.png 508w, https:\/\/mindfusion.dev\/blog\/wp-content\/uploads\/2013\/04\/line_chart_performance-300x175.png 300w\" sizes=\"auto, (max-width: 508px) 100vw, 508px\" \/><\/a><p id=\"caption-attachment-185\" class=\"wp-caption-text\">The final chart<\/p><\/div>\n<p><strong>Create the series<\/strong><\/p>\n<p>We create 8 series and add them to the Series collection of the chart:<\/p>\n<pre>for (int i = 0; i &lt; 8; i++)\n{\n     LineSeries series = new LineSeries();\n     series.StrokeThickness = 3;\n     series.Strokes.Add(RandomStroke);\n     series.Fills = series.Strokes;\n     series.Title = \"Series\" + i;\n     series.XData = new DoubleCollection();\n     series.YData = new DoubleCollection();\n\n     for (int j = 0; j &lt; 100; j++)\n     {\n        series.XData.Add(j);\n        series.YData.Add(10 + 10 * i + random.Next(-5, 5));\n      }\n\n     lineChart.Series.Add(series);\n }\n\n<\/pre>\n<p>Each series is drawn with a random generated stroke:<\/p>\n<pre>  private Brush RandomStroke\n  {\n     get\n     {\n        byte r = (byte)random.Next(0, 255);\n        byte g = (byte)random.Next(0, 255);\n        byte b = (byte)random.Next(0, 255);\n        return new SolidColorBrush(Color.FromArgb(255, r, g, b));\n       }\n     }\n<\/pre>\n<p>We must set the Title and Fills properties though we don\u2019t need them for the chart \u2013 they are used by the legend. The Y-values of the line points are random numbers between 10 and 90.<\/p>\n<p><strong>The Legend<\/strong><\/p>\n<pre>ChartLegend legend = new ChartLegend();\nlegend.Series = lineChart.Series;\nlineChart.Legends.Add(legend);\n<\/pre>\n<p>Our legend is of type ChartLegend, which means we must set its Series property to the list of ChartSeries that the legend explains. The ChartLegend reads the labels for its items from the Title property of a ChartSeries. It draws the series rectangles with the first brush in ChartSeries.Strokes and fills them with the first brush in ChartSeries.Fills. That\u2019s why it was important to set the Title, Fills and Strokes properties as shown above.<\/p>\n<p>The Axis<br \/>\nThe axis shows scale divisions with interval 10. Initially they range from 0 to 100 in 10 intervals.<\/p>\n<pre>lineChart.XAxisSettings.MinValue = 0;\nlineChart.XAxisSettings.Interval = 10;\nlineChart.XAxisSettings.LabelType = LabelType.AutoScale;\n<\/pre>\n<p><strong>Updating Data in Real Time<\/strong><\/p>\n<p>We create a timer that simulates reading real time data. The timer ticks ten times every second:<\/p>\n<pre>timer.Interval = TimeSpan.FromMilliseconds(100);\ntimer.Tick += new EventHandler(OnTimerTick);\n<\/pre>\n<p>When the timer ticks, we generate ten new values for each of the eight series, add them to each series and remove the last ten values:<\/p>\n<pre>foreach (LineSeries series in lineChart.Series)\n{\n    for (int i = 10 - 1; i &gt;= 0; i--)\n    {\n         series.XData.Remove(series.XData[i]);\n         series.YData.Remove(series.YData[i]);\n    }\n}\n\nforeach (LineSeries series in lineChart.Series)\n{\n    int counter = 0;\n    for (int i = 0; i &lt; 10; i++)\n    {\n         series.XData.Add(i + maxX);\n         series.YData.Add(10 + counter * 10 + random.Next(-5, 5));\n     }\n     counter++;\n}\n<\/pre>\n<p>Here we use a global variable maxX, which we have declared in the class:<\/p>\n<pre>private int maxX = 100;\n<\/pre>\n<p>We increase our global variable with 10 to keep up with the change in the data:<\/p>\n<pre>maxX += 10;\n<\/pre>\n<p><strong>Updating the Axis<\/strong><br \/>\nAt each tick of the timer we increase the start of the axis with 10:<\/p>\n<pre>lineChart.XAxisSettings.MinValue += 10;\n<\/pre>\n<p><strong>Performance Optimization<\/strong><br \/>\nPerformance is greatly improved if we set the thickness of the lines to 1 rather than 3. The reason for this is that Windows GDI system draws thick lines by filling polygons. This requires numerous calculations about the coordinates of each polygon. When the thickness is small, Windows GDI draws lines rather than polygons, which speeds up drawing and performance.<\/p>\n<pre>series.StrokeThickness = 3;\n<\/pre>\n<p><strong>Download of the Sample<\/strong><br \/>\nYou can download the sample with complete source code from here:<\/p>\n<p align=\"center\"><a href=\"https:\/\/mindfusion.dev\/_samples\/LineChartPerformance.zip\">Download the Sample<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In today\u2019s post we are going to build a real time line chart with the following features: 8 data series with 100 points each. Ten times per second we add 10 random points to each series and remove the last &hellip; <a href=\"https:\/\/mindfusion.dev\/blog\/real-time-line-chart\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[61],"tags":[63,65,62,64,66,67],"class_list":["post-175","post","type-post","status-publish","format-standard","hentry","category-charting-2","tag-data","tag-legend","tag-line-chart","tag-performance","tag-real-time","tag-series"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3RlKs-2P","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/175","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/comments?post=175"}],"version-history":[{"count":10,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/175\/revisions"}],"predecessor-version":[{"id":2424,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/175\/revisions\/2424"}],"wp:attachment":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/media?parent=175"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/categories?post=175"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/tags?post=175"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}