The demo can be downloaded at http://www.devexpress.com/Support/Center/e/E1836.aspx. This is a zip file. You can add the .zip extension and extract it or download the Example Runner (from the above link).
The ChartControl has a new feature referred to as Swift Plot. The benefit of the SwiftPlotDiagram class is render charts with a lot of data very quickly or to plot real-time data with a significant number of data changes. The first code sample in Listing 1 is from our Support Center —see Listing 1.
Figure 1: The ChartControl diagramming in real-time (against .NET Framework 2.0).
The first sample uses a ChartControl and Timer and plots 20 points at a time scrolling the plotted points from right to left at each timer interval. Points less then the minimum DateTime on the left of the chart are removed, simulating the real-time charting action.
Listing 1: The ChartControl using the SwiftPlotDiagram class to simulate real-time charting.
using System;
using System.Windows.Forms;
using DevExpress.XtraCharts;
// ...
namespace ASwiftPlotChart {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
const int interval = 20;
Random random = new Random();
int TimeInterval = 10;
double value1 = 10.0;
AxisRange AxisXRange {
get {
SwiftPlotDiagram diagram = chartControl1.Diagram as SwiftPlotDiagram;
if (diagram != null)
return diagram.AxisX.Range;
return null;
}
}
double CalculateNextValue(double value) {
return value + (random.NextDouble() * 10.0 - 5.0);
}
void UpdateValues() {
value1 = CalculateNextValue(value1);
}
private void timer1_Tick(object sender, EventArgs e) {
Series series1 = chartControl1.Series[0];
if (series1 == null)
return;
DateTime argument = DateTime.Now;
SeriesPoint[] pointsToUpdate1 = new SeriesPoint[interval];
for (int i = 0; i < interval; i++) {
pointsToUpdate1[i] = new SeriesPoint(argument, value1);
argument = argument.AddMilliseconds(1);
UpdateValues();
}
DateTime minDate = argument.AddSeconds(-TimeInterval);
int pointsToRemoveCount = 0;
foreach (SeriesPoint point in series1.Points)
if (point.DateTimeArgument < minDate)
pointsToRemoveCount++;
if (pointsToRemoveCount < series1.Points.Count)
pointsToRemoveCount--;
series1.Points.AddRange(pointsToUpdate1);
if (pointsToRemoveCount > 0) {
series1.Points.RemoveRange(0, pointsToRemoveCount);
}
if (AxisXRange != null) {
AxisXRange.SetMinMaxValues(minDate, argument);
}
}
}
}
The chart from our sample program has a lot of jaggedy lines because the values are randomly generated. Real data may or may not produce an especially varied chart; it just depends on the data. The demo in Listing 2 uses stock quotes—the last trade price—from Yahoo! Finance. The example sends a quote request to Yahoo! every 1,000 milliseconds and updates the series points with the last trade price. (You can try this quoting system by typing in a URL similar to http://download.finance.yahoo.com/q?s=dell.) The request is sent from the Windows application using an HttpWebRequest, which means you will have to be online to run the demo, and it will help if the markets are open.
To create the sample
- Drop a v2009 volume 3 ChartControl on a WinForm. The wizard will pop up. Pick the Swift Plot chart type and click Finish (see Figure 2)
- Click the ChartControl’s smart tag and select Series—see Figure 3—and add a Series item to the Series Collection Editor
- In the Series Collection Editor switch to the Properties tab and change the ArgumentScaleType to DateTime (shown in Figure 3)
- Close the Series Collection Editor
- Click on the SwiftPlotDiagram (center part of the control)
- Change DateTimeGridAlignment to Millisecond, DateTimeMeasureUnit to Millisecond, and DateTimeOptions.Format to Custom, using the format string ss:f to display seconds and sub-seconds (see Figure 4)
- Add and enable a timer.
- Add the code from Listing 2 and run the demo.
Listing 2: Plotting the last trade price for stocks-quotes obtained from Yahoo! Finance.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DevExpress.XtraCharts;
using System.Diagnostics;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
namespace SwiftPlotDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private AxisRange AxisXRange
{
get
{
SwiftPlotDiagram diagram = chartControl1.Diagram as SwiftPlotDiagram;
if (diagram != null)
return diagram.AxisX.Range;
return null;
}
}
private void Update()
{
const string stock1 = "DELL";
var quote = new { Stock = stock1, Quote = GetQuote(stock1) };
var range = new { Stock = stock1, Quote = GetRange(stock1) };
PlotSeries(chartControl1.Series[0], quote.Stock, quote.Quote, range.Quote);
}
private char[] charArray = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
private void PlotSeries(Series series, string stock, string quote, string range)
{
if (series == null) return;
series.Name = stock;
DateTime argument = DateTime.Now;
string str = quote.Split(',', '-')[1];
string digits = str.Substring(str.IndexOfAny(charArray), str.IndexOf("</b>") - 4).Trim();
string[] temp = range.Split('-', '"');
double low = Convert.ToDouble(temp[1].Trim());
double high = Convert.ToDouble(temp[2].Trim());
SwiftPlotDiagram diagram = chartControl1.Diagram as SwiftPlotDiagram;
diagram.AxisY.Range.MaxValue = high;
diagram.AxisY.Range.MinValue = low - .5;
double last = Convert.ToDouble(digits);
SeriesPoint point = new SeriesPoint(argument, last);
series.Points.AddRange(new SeriesPoint[]{point});
argument.AddMilliseconds(1);
if (series.Points.Count > 200)
series.Points.RemoveRange(0, 100);
}
protected void Timer1_Tick(object sender, EventArgs e)
{
Update();
}
public string GetQuote(string stock)
{
try
{
return InnerGetQuote(stock, "l");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return "N/A";
}
}
private string GetRange(string stock)
{
try
{
return InnerGetQuote(stock, "m");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return "N/A";
}
}
private string InnerGetQuote(string stock, string format)
{
string url = @"http://quote.yahoo.com/d/quotes.csv?s={0}&f={1}";
var request = HttpWebRequest.Create(string.Format(url, stock, format));
using (var response = request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream(),
Encoding.ASCII))
{
return reader.ReadToEnd();
}
}
}
private void timer1_Tick_1(object sender, EventArgs e)
{
Update();
}
}
}
In summary, the code works works by calling GetRange (trade range) and GetQuote at every timer tick, adjusting the intra-day trading range by setting the SwiftPlotDiagram’s AxisX,RangeMaxValue and AxisX.Range.MinValue and plugging the last trade price into the series.
A longer explanation is that timer1_Tick calls Update. Update calls GetQuote and GetRange—hard coded for Dell—and calls PlotSeries. GetQuote and GetRange both form a query string that is sent via an HttpWebRequest to yahoo! Finance, passing this data to PlotSeries. PlotSeries sets the series name, parses the quote and range data. The range data adjust the AxisX min and max values, and the quote is placed in the Series Points collection. (After there are more than 200 series points a 100 points are removed.) Things like the string manipulation is necessary because Yahoo! Finance embeds some extra characters and HTML in the return data.
the min and max range values are adjusted to zoom in because intra-day trade prices tend to change in small amounts. You can also set SwiftPlotDiagram.EnableZooming, which will turn on run-time zooming. Shift click will zoom in, Alt+click will zoom out by a factor of 3 and Ctrl + + and Ctrl + – will zoom in or out by a factor of 20%.
Figure 2: Select the Swift Plot chart type in the chart wizard.
Figure 3: Set ArgumentScaleType to DateTime.
Figure 4: Set DateTimeOptions to Custom and use the format string ss:f.
Figure 5: Dynamically set the range to focus in on small changes (or use the zoom feature).