You are on page 1of 7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

Tutorial: Creating Charts with RealTime Data


Visual Studio 2010

Applies to: Functional Programming


Published: January 2010
Authors: Tomas Petricek and Jon Skeet

Get this book in Print, PDF, ePub and Kindle at manning.com. Use code MSDN37b to save 37%.
Summary: This tutorial demonstrates how to dynamically update a chart to display realtime data. It discusses how to use
F# asynchronous workflows and how to receive updates on a background thread.
This topic contains the following sections.
Working with RealTime Data
Displaying CPU Usage with a Line Area Chart
Retrieving Data in the Background
Summary
Additional Resources
See Also
This article is associated with Real World Functional Programming: With Examples in F# and C# by Tomas Petricek with Jon
Skeet from Manning Publications (ISBN 9781933988924, copyright Manning Publications 2009, all rights reserved). No part of
these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any meanselectronic,
electrostatic, mechanical, photocopying, recording, or otherwisewithout the prior written permission of the publisher, except in
the case of brief quotations embodied in critical articles or reviews.

Working with RealTime Data


This tutorial shows how to create a chart that displays realtime data. It can be used as a guide when creating an
application that monitors a frequently changing web site, displays data obtained from sensors, or visualizes stock prices,
and so on. When using Microsoft Chart Controls, it is possible to continue adding data while the chart is displayed. The
only limitation is that a chart can be accessed only from the thread where it was created. There are also certain types of
charts that are particularly suitable for visualizing large amounts of data using points or lines. These may be useful when
the source generates a large number of inputs.
In this tutorial, youll learn everything you need to display realtime data:
Use asynchronous workflows running on a single thread to update a chart repeatedly
https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

1/7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

Update the range of x and yaxes depending on the values being added to a chart
Use asynchronous workflows to generate values in the background
Create charts suitable for efficiently displaying a large number of data points
The tutorial walks through the implementation of two examples that demonstrate working with realtime data. The first
example creates a chart that displays CPU usage. The second example generates data using a random walk algorithm
and demonstrates a frequently updated chart with a large number of points.

Displaying CPU Usage with a Line Area Chart


When creating a Windows Forms application, the controls of the application can be accessed only from the thread
where the user interface was constructed. This thread runs a Windows message loop, so all user interface events are
also triggered on this thread. For this reason, event handlers can safely update the user interface.
An event handler shouldn't run long running operations because that would make the user interface nonresponsive.
Obtaining CPU usage can be done quite efficiently, so the example doesn't need any additional threads. It can simply
run the whole application on the single GUI thread. This can be done either using a Timer object from the
System.Windows.Forms namespace which uses the Windows message queue to report the events or using
asynchronous workflows and the F# Async.StartImmediate primitive. The primitive ensures that all of the workflow
code is executed on the GUI thread. The tutorial follows the latter approach. However, the first snippet shows a function
that gets the current CPU usage:
F#
openSystem.Diagnostics
///FunctionthatreturnsthecurrentCPUusage
letgetCpuUsage=
letcounter=
newPerformanceCounter
(CounterName="%ProcessorTime",
CategoryName="Processor",InstanceName="_Total")
(fun()>counter.NextValue())
The implementation of the getCpuUsage function first initializes a performance counter. Links to articles with more
information about performance counters and the names used can be found at the end of the article. After creating the
counter, the code creates a function using a lambda expression. This way, the value counter is private to the function
but is not recreated each time the function is called. The snippet sets all of the properties that are needed to identify the
counter in the system when creating the counter. The current value of the counter can be obtained using the NextValue
member.
The next step is to create objects representing the chart. The tutorial uses a helper function createChart that is
implemented in the previous tutorial Tutorial: Creating a Series Using Data Binding and it can also be obtained from
the complete source code available at the end of the article. The following snippet configures the chart to use a spline
area chart which fills an area under a smooth line:
F#
letchart,series=createChartSeriesChartType.SplineArea
letarea=chart.ChartAreas.[0]
area.BackColor<Color.Black
area.AxisX.MajorGrid.LineColor<Color.DarkGreen
area.AxisY.MajorGrid.LineColor<Color.DarkGreen
https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

2/7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

chart.BackColor<Color.Black
series.Color<Color.Green
The code creates a chart with black background, dark green grid, and light green filled area that shows the CPU usage,
which is a common look for this kind of a technical chart. The createChart function creates a chart with a single area
but it doesn't return a reference to the area. Instead, the snippet needs to get the area from the ChartAreas collection.
The last piece of code that needs to be written is the most interesting part. It creates an asynchronous workflow that
periodically updates the chart area. The implementation uses a while loop in the workflow and the workflow is started
such that all of the user code runs on the main GUI thread meaning that it can safely access the series object:
F#
letupdateLoop=async{
whilenotchart.IsDisposeddo
letv=float(getCpuUsage())
series.Points.Add(v)|>ignore
do!Async.Sleep(250)}
Async.StartImmediateupdateLoop
The loop runs until the chart is disposed i.e., when the form is closed by the user. The body obtains the current CPU
usage and adds it as a new data point in the series. The Add method creates a new DataPoint object and returns it as
the result. The snippet doesn't need it, so it uses the ignore function. Adding the new point immediately redraws the
chart area, so this is all that needs to be done. After adding the point, the workflow calls Async.Sleep to suspend itself
for a quarter of a second before going back through the while loop.
Updating the chart can be done only from the main GUI thread. When running the data retrieval in the background, the
code should then schedule an update of the chart series on the main thread. If the retrieval doesnt take a long time, the
entire workflow can run on the GUI thread. The listing above chooses the latter approach, which can be implemented by
starting the workflow using Async.StartImmediate. The CPU usage graph created in this section is shown in Figure 1.
Figure 1. A graph showing realtime CPU usage data

The chart created in this section is dynamically updated, but it is only adding new points. After running for a long time, it
will become visually crowded. The next section discusses another example. It retrieves data on the background thread
and also shows how to keep only a certain number of data points in the chart.
https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

3/7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

Retrieving Data in the Background


This example implements a workflow that calculates a random walk and displays the generated values. The calculation is
run on a background thread, so the approach is suitable for more complicated calculations that take some time to
complete. The example uses the FastLine chart type. This type of chart is a line chart that is optimized for a very large
number of data points. It doesnt support some additional features that usual Line chart does such as labels, shadows,
and visual properties of individual points. On the other hand, the series can be drawn more efficiently. A related data
series type for charting large amounts of data is FastPoints, which displays data elements as dots.
F#
letchart,series=createChartSeriesChartType.FastLine
letaxisX=chart.ChartAreas.[0].AxisX
letaxisY=chart.ChartAreas.[0].AxisY
chart.ChartAreas.[0].InnerPlotPosition<
newElementPosition(10.0f,2.0f,85.0f,90.0f)
The listing first creates a chart and gets a reference to objects representing the X and yaxes. These two objects will be
needed quite frequently in the rest of the code, so this makes the code below easier to read. Finally, the snippet also
specifies the InnerPlotPosition property of the chart area. This defines the location where the content of the chart is
located. The numbers specify the offsets from the left and the top together with the width and the height in percent of
the overall control size.
A fixed location is needed because the example dynamically changes the range of the axes, and the length of the labels
associated with the axes will change e.g., 5 and 1.5. Automatic positioning would result in a jittery chart area as the
chart resizes to accommodate the different label sizes.
The ranges need to be updated by the program because the example also removes points from the data series. In this
case, the range stops automatically updating after points are removed. The following function takes the number of
points generated so far as the argument. It finds the maximum and minimum Y values in the series and updates the
ranges:
F#
letupdateRanges(n)=
letvalues=seq{forpinseries.Points>p.YValues.[0]}
axisX.Minimum<floatn500.0
axisX.Maximum<floatn
axisY.Minimum<values|>Seq.min|>Math.Floor
axisY.Maximum<values|>Seq.max|>Math.Ceiling
The example keeps the last 500 points in the chart series. The values on the xaxis are the automatically generated
numbers that are incremented each time a point is added. When setting the range of the xaxis, the maximum value is
the number of points generated so far and the minimum value is 500 less than that. To find the maximum and minimum
Y values, the snippet first creates a sequence with all Y values and then uses Seq.min and Seq.max to find the minimum
and maximum. To add some space around the series and to ensure that the labels show readable numbers, the
minimum is rounded down and the maximum is rounded up.
The next step in the tutorial looks at adding new data points to the chart. As in the previous example, the computation
that generates data and updates the chart is written as an asynchronous workflow. The example shows how to
implement the workflow when the calculation takes some time. The calculation of random walk doesn't take a long time
so the function artificially blocks using Sleep but the approach would work for CPUintensive computations. This is
possible because the computation is run in a background thread. However, the chart needs to be updated from the GUI
thread. This can be done by switching between the threads.
https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

4/7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

The following asynchronous function switches to the GUI thread, checks if the chart is still open, updates the chart, and
then switches back to a background thread. It also returns a Boolean value indicating whether or not the chart has been
disposed, so that the calling workflow can terminate when the chart is closed:
F#
openSystem.Threading
letctx=SynchronizationContext.Current
letupdateChart(valueX,valueY)=async{
do!Async.SwitchToContext(ctx)
ifchart.IsDisposedthen
do!Async.SwitchToThreadPool()
returnfalse
else
series.Points.AddXY(valueX,valueY)|>ignore
whileseries.Points.Count>500doseries.Points.RemoveAt(0)
updateRanges(valueX)
do!Async.SwitchToThreadPool()
returntrue}
The snippet starts by creating a value ctx that keeps the synchronization context of the main GUI thread. The
updateChart function takes the X and Y values as arguments and starts by switching to the GUI context, so that the code
can safely access chart and series objects. If the form has been closed, the workflow switches back to the thread pool
although this isnt strictly necessary because the workflow will terminate anyway and returns false. If the form is still
open, the function adds the new point and removes the oldest points until the count is at most 500. Then it updates the
ranges of the axes, switches back to the thread pool and returns true, so that the workflow continues running.
The only remaining piece of code to make the sample complete is a loop that generates data and updates the chart
until the form is closed. The loop needs to keep some state, so it is written as a recursive asynchronous workflow. The
state consists of the number of generated data points and the value of the last point:
F#
letrandomWalk=
letrnd=newRandom()
letrecloop(count,value)=async{
letcount,value=count+1,value+(rnd.NextDouble()0.5)
Thread.Sleep(20)
let!running=updateChart(floatcount,value)
ifrunningthenreturn!loop(count,value)}
loop(0,0.0)
Async.Start(randomWalk)
The loop is written by an inner recursive function named loop that keeps the state of the process. The random walk
calculation adds a random number in a range 0.5 to +0.5 to the current value in every step. After doing that, the
workflow calls the Thread.Sleep function to block the thread for some time. Note that the example actually blocks the
thread simulating some CPUintensive calculation, so it is important that the workflow is executed on a background
thread. Note that this is very different than the previous example, which used Async.Sleep, which suspends the
workflow, but doesn't block an actual thread.
Next, the workflow uses let! to call the asynchronous function updateChart. The function switches to the GUI thread,
updates the chart, and, before returning, it switches back to a background thread. The result of the call is used to decide
whether the workflow should continue looping or not. Finally, the snippet starts the workflow using the Async.Start

https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

5/7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

function. This differs from StartImmediate used in a previous example in that it starts the computation in a
background thread. The screenshot in Figure 2 shows the result.
Figure 2. A chart showing random walk generated in background

Summary
This tutorial discussed several topics that are important when using Microsoft Chart Controls to display realtime data.
The first example looked at a scenario where the data can be retrieved efficiently so the processing can be performed
on the GUI thread. This pattern can be elegantly implemented by starting an asynchronous workflow using
Async.StartImmediate.
The second example created a more advanced application that also removes old data points. When doing that, the
code also needs to explicitly calculate the range of the axes if the ranges change dynamically. In that case, it is useful to
explicitly specify the inner chart area so that the changing length of axis labels doesnt affect the view. The second
example also shows how to implement realtime chart when generating data takes a longer time. In this case, the
calculation should run on a background thread, which can be implemented by starting the asynchronous workflow in the
background using Async.Start and by switching to the GUI thread once the data is available.

Additional Resources
This article is the last article in a series that discusses using Microsoft Chart Controls library directly. The previous
tutorial focused on data binding and the first article introduced basic aspects of the library:
Tutorial: Getting Started with Microsoft Chart Controls
Tutorial: Creating a Series Using Data Binding
The sample in this tutorial uses the Chart Controls library directly using the types available in .NET 4.0. An alternative
option is to use the FSharpChart library that builds on top of Chart Controls but adds more F#friendly API. For more
information see the following articles:
Overview: Getting Started with the FSharpChart Library
Tutorial: Visualizing Stock Prices Using F# Charts
How to: Create Standard Charts in F#

https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

6/7

9/19/2016

Tutorial:CreatingChartswithRealTimeData

To download the code snippets shown in this article, go to http://code.msdn.microsoft.com/Chapter6VisualizingData


c68a2296

See Also
This article is based on Real World Functional Programming: With Examples in F# and C#. Book chapters related to the
content of this article are:
Book Chapter 4: Exploring F# and .NET libraries by example demonstrates how to create a simple charting
application from scratch. This chapter is useful for learning F# programming when focusing on working with data.
Book Chapter 12: Sequence expressions and alternative workflows explains how to work with inmemory data
sets in F# using sequence expressions and higherorder functions.
Book Chapter 13: Asynchronous and datadriven programming shows how to use asynchronous workflows to
obtain data from the internet, how to convert data to a structured format, and how to chart it using Excel.
The following MSDN documents are related to the topic of this article:
Performance Counters explains how Windows performance counters work in general and provides a list of
available counters.
PerformanceCounter Class provides detailed documentation about the class that is used to retrieve performance
counter statistics.
Chart Controls is a MSDN section dedicated to working with Microsoft Chart Controls with many examples in C#
and Visual Basic. It includes a comprehensive reference with all chart types and properties.
Async.SwitchToContext Method F# is a method that allows us to switch from a background thread to a Windows
Forms thread so that we can safely update the chart. The MSDN page contains some additional examples.

Previous article: Tutorial: Creating a Series Using Data Binding


Next article: Overview: Getting Started with the FSharpChart Library
2016 Microsoft

https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx

7/7

You might also like