Professional Documents
Culture Documents
Tutorial:CreatingChartswithRealTimeData
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.
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.
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
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
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.
https://msdn.microsoft.com/enus/library/hh297119(d=printer,v=vs.100).aspx
7/7