This walkthrough demonstrates steps necessary to add a simple stock chart to a WPF application.
Prerequisites
Adding Data Layer
|
This procedure assumes that you have already created or opened a WPF application and a Window in it. |
|
To make things simple we will use a simple object collection as data layer for this walkthrough. In real-life applications this could be represented by any data object implementing IEnumerable interface. |
Creating data layer
-
First we will crete a class to represent a single data item. Open source view for your window and add the following class declaration
CopyC#public class DItem { public DateTime date { get; set; } public double open { get; set; } public double high { get; set; } public double low { get; set; } public double close { get; set; } public double volume { get; set; } }
Our data class has properties to hold date associated with data, open/high/low/close prices of some fictional stock at that date and a volume of sales at that date.
-
Next we will add a property to our Window class to hold a collection of data items like this:
CopyC#public List<DItem> Data { get; set; } -
We'll fill our data collection with fictional data using this method:
CopyC#private void AddData() { Data = new List<DItem>(); Random rnd = new Random(); DateTime currentDate = new DateTime(2009, 3, 8); double baseValue = 20 + rnd.NextDouble() * 10; for (int i = 0; i < 1000; i++) { currentDate = currentDate.AddDays(rnd.Next(1, 3)); double value = baseValue + rnd.NextDouble() * 6 - 3; Data.Add(new DItem() { date = currentDate, open = value + rnd.NextDouble() * 4 - 2, high = value + 2 + rnd.NextDouble() * 3, low = value - 2 - rnd.NextDouble() * 3, close = value + rnd.NextDouble() * 4 - 2, volume = rnd.NextDouble() * 300 } ); baseValue = value < 6 ? value + rnd.NextDouble() * 3 : value; } }
-
We want to call AddData() method when the window loads. To do that we add event handler for Window's Loaded event.
Open your window in design view and add an event handler by double-clicking in Properties pane next to Loaded event.
Window_Loaded method should be added to your class and Loaded="Window_Loaded" attribute to your XAML Window tag.
Adding Stock Chart Control
|
This procedure assumes that you have already created or opened a WPF application and a Window in it. |
Add StockChart control to the Window
-
Drag StockChart control from your Visual Studio toolbox onto your WPF Designer surface.
-
Adjust Width and Height properties of the created ams:StockChart via XAML editor or properties window. Alternatively you can just delete these property settings to allow the chart to occupy all of the available space.
Add and configure DataSet
-
AmCharts.Windows.Stock.Data..::.DataSet is an intermediate data layer between your raw data and StockChart charts and graphs. It is used to calculate and store aggregate data based on your raw data. This aggregate data is then used to draw graphs, display values, etc.
Add a DataSet to your stock chart like this:
CopyC#<ams:StockChart> <ams:StockChart.DataSets> <ams:DataSet Title="Random Data" ShortTitle="RD" /> </ams:StockChart.DataSets> </ams:StockChart>Title property is displayed in DataSetSelector (not visible in this sample) and value of ShortTitle is displayed in Legend.
-
Now we need to bind our new DataSet to our data layer created earlier. To do that we specify data-binding related properties of the DataSet:
CopyXAML<ams:DataSet Title="Random Data" ShortTitle="RD" ItemsSource="{Binding Data}" DateMemberPath="date" OpenMemberPath="open" HighMemberPath="high" LowMemberPath="low" CloseMemberPath="close" ValueMemberPath="close" VolumeMemberPath="volume" />
ItemsSource property is bound to our data layer and XxxMembarPath specify which properties in our data layer correspond to which properties of DataItem object which is a class representing raw data of the DataSet.
Note:Notice that even though we don't have a "value" property in our data layer we set ValueMemberPath="close". This is optional but it saves us from having to specify DataField property in our Candlestick graph and Scroller. By default Value is used as DataField in graphs so if Value is not set you must set DataField to some other value in order for graphs to be drawn and behave properly.
-
Now we just need to set the DataContext of our Window so that ItemsSource="{Binding Data}" knows where to look for the Data collection. We do that in our Loaded event handler:
CopyC#private void Window_Loaded(object sender, RoutedEventArgs e) { this.DataContext = this; AddData(); }
Add Charts
-
We are going to add 2 charts: one candlestick chart for open/high/low/close data and one column chart for volume data.
For the first candlestick chart add the following code to your stock chart's XAML:
CopyXAML<ams:StockChart.Charts> <ams:Chart Title="Stock Prices" GridHeight="2*"> <ams:Chart.Graphs> <ams:Graph GraphType="Candlestick" /> </ams:Chart.Graphs> </ams:Chart> </ams:StockChart.Charts>
Title is displayed in the Legend and GridHeight="2*" sets the chart to be twice as high as other "standard" sized charts. The GraphType set to Candlestick speaks for itself. Candlestick graph will automatically use Open/High/Low/Close properties of the DataItem.
-
Second (column) chart is added like this:
CopyXAML<ams:Chart Title="Volume"> <ams:Chart.Graphs> <ams:Graph GraphType="Column" DataField="Volume" PeriodValue="Sum" /> </ams:Chart.Graphs> </ams:Chart>
By default single value graphs (like line, column, step line, etc.) use Value property of the DataItem. But since we want to visualize Volume in this chart we set DataField="Volume". Also when data is grouped into longer periods (weeks, months, years) we want to display sum of all day volumes in this period, so we set PeriodValue="Sum" (default is the last value of the period).
Complete code of this Walkthrough
Here's a complete source code of this example:
<Window x:Class="AmStockWPF.SimpleDataBoundChart" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ams="http://schemas.amcharts.com/stock/wpf/2009/xaml" Title="SimpleDataBoundChart" Loaded="Window_Loaded"> <Grid Margin="10"> <ams:StockChart> <ams:StockChart.DataSets> <ams:DataSet Title="Random Data" ShortTitle="RD" ItemsSource="{Binding Data}" DateMemberPath="date" OpenMemberPath="open" HighMemberPath="high" LowMemberPath="low" CloseMemberPath="close" ValueMemberPath="close" VolumeMemberPath="volume" /> </ams:StockChart.DataSets> <ams:StockChart.Charts> <ams:Chart Title="Stock Prices" GridHeight="2*"> <ams:Chart.Graphs> <ams:Graph GraphType="Candlestick" /> </ams:Chart.Graphs> </ams:Chart> <ams:Chart Title="Volume"> <ams:Chart.Graphs> <ams:Graph GraphType="Column" DataField="Volume" PeriodValue="Sum" /> </ams:Chart.Graphs> </ams:Chart> </ams:StockChart.Charts> </ams:StockChart> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace AmStockWPF { /// <summary> /// Interaction logic for SimpleDataBoundChart.xaml /// </summary> public partial class SimpleDataBoundChart : Window { public SimpleDataBoundChart() { InitializeComponent(); } public List<DItem> Data { get; set; } private void AddData() { Data = new List<DItem>(); Random rnd = new Random(); DateTime currentDate = new DateTime(2009, 3, 8); double baseValue = 20 + rnd.NextDouble() * 10; for (int i = 0; i < 1000; i++) { currentDate = currentDate.AddDays(rnd.Next(1, 3)); double value = baseValue + rnd.NextDouble() * 6 - 3; Data.Add(new DItem() { date = currentDate, open = value + rnd.NextDouble() * 4 - 2, high = value + 2 + rnd.NextDouble() * 3, low = value - 2 - rnd.NextDouble() * 3, close = value + rnd.NextDouble() * 4 - 2, volume = rnd.NextDouble() * 300 } ); baseValue = value < 6 ? value + rnd.NextDouble() * 3 : value; } } public class DItem { public DateTime date { get; set; } public double open { get; set; } public double high { get; set; } public double low { get; set; } public double close { get; set; } public double volume { get; set; } } private void Window_Loaded(object sender, RoutedEventArgs e) { this.DataContext = this; AddData(); } } }
When you run your application your window should look something like this: