Visiblox for Windows Phone 7 Beta was released a few days ago ( see the announcement post here). This posts shows how to get started with it and create line, bar and pie series, enable selection and add zooming and panning behaviours to them.
The structure of the article is as follows:
Line Series
Before getting started you need to install the Silverlight Toolkit for Windows Phone and then download the component dll from the download page. After having done so, unpack the zip and reference Visiblox.Charts/Visiblox.Charts.WP7/Visiblox.Charts.dll.
Creating a Chart
To get started with charting you first need to create a Chart object. As with most things in Silveright, this can be done both in XAML and in code-behind: in this post we'll be doing most of what's possible in XAML. Creating an empty chart can be done by declaring a Chart element in XAML (optionally setting its Width and Height):
<phone:PhoneApplicationPage
(...)
xmlns:charts="clr-namespace:Visiblox.Charts;assembly=Visiblox.Charts">
(...)
<charts:Chart Width="700" Height="400">
</charts:Chart/>
</phone:PhoneApplicationPage >
Adding the Series to the Chart
Once we have the Chart object, we need to add series to it. Adding a series consists of two steps: adding series (of type IChartSeries) and setting the data of this series.
The series can be a LineSeries, BarSeries, ColumnSeries, BandSeries or StaircaseSeries in the beta WP7 component. The data can be set either by manually creating a DataSeries or by binding to a list of model items by using BindableDataSeries.
Creating a LineSeries with Manually Created Data
The code for creating a LineSeries with manually created data is the following. First, let's add a LineSeries to the Chart in XAML:
<charts:Chart x:Name="MyChart" Width="700" Height="400">
<charts:Chart.Series>
<charts:LineSeries/>
</charts:Chart.Series>
</charts:Chart>
Now we need to manually create the data for the series and assign this data to it. Let's do this in the code behind:
// Create a DataSeries<DateTime, double> and assign it to the first series of the chart
var rnd = new Random();
var dataSeries = new DataSeries<DateTime, double>();
for (int i = 0; i < 20; i++)
{
dataSeries.Add(new DataPoint<DateTime,double>(DateTime.Today.AddDays(i),rnd.NextDouble()*20));
}
MyChart.Series[0].DataSeries = dataSeries;
The result is the following line chart:
Adding Zooming and Panning to the Chart
So far we have created a static chart. Zooming and panning of charts are quite common requirements and similar to the Silverlight version, Visiblox Charts for WP7 supports these from out of the box via the ZoomBehaviour and PanBehaviour. To add zooming and panning we need to set the Chart's Behaviour with the following line of code:
<charts:Chart x:Name="MyChart" Width="700" Height="400">
<charts:Chart.Series>
<charts:LineSeries/>
</charts:Chart.Series>
<!-- Enable zooming and panning on the chart -->
<charts:Chart.Behaviour>
<charts:PanBehaviour/> <!-- Alternatively ZoomBehaviour -->
</charts:Chart.Behaviour>
</charts:Chart>
Having added this, the chart can now be zoomed in by pinching and zoomed out by double tapping, or panned by dragging.
Enabling Selection on the Series
Another common requirement with charts and series is support for user selection of points. This is supported via the same API on Visiblox Charts for WP7 as it is on the Silverlight version.
Selection of the Whole Series
To enable selection of the whole series, we simply need to set SelectionMode to Series. When the user taps the line, the IsSelected dependecy property on LineSeries will now be changed:
<charts:Chart x:Name="MyChart" Width="700" Height="400">
<charts:Chart.Series>
<!-- Line series with series selection enabled -->
<charts:LineSeries SelectionMode="Series"/>
</charts:Chart.Series>
</charts:Chart>
Selection of Individual Points
Selection of individual points can also be done. To do so, first let's display the points by setting theShowPointsproperty onLineSeriesto true. After this we can enable single point selection by setting theSelectionModeproperty onLineSeriestoSinglePoint(or allow selection of multiple points by setting it toMultiplePoints):
<charts:Chart x:Name="MyChart" Width="700" Height="400">
<charts:Chart.Series>
<!-- Line series with point selection enabled -->
<charts:LineSeries ShowPoints="True" SelectionMode="SinglePoint" />
</charts:Chart.Series>
</charts:Chart>
Now whenever the selection is changed, theSelectedItemproperty andSelectedItemscollection changes on the series and theSelectionChangedevent fires.
Here is how the example looks at this point:
Basic Styling of the Chart: Adding Titles, Positioning the Legend
The plot area of our chart looks decent at this point, however the surrounding area could use some formatting.
Adding Titles to the Chart, Axes and Series
Setting the title of the Chart can be done by simply setting its Title property.
The XAxis and YAxis also have a Title property. You may have noticed, however, that until now we haven't defined any axes: by default the chart auto - creates these axes based on the data type. In order to provide titles to them, we need to manually assign an IAxis instance to the XAxis and YAxis property on the chart:
<charts:Chart x:Name="MyChart" Width="700" Height="400" Title="My First Chart">
<charts:Chart.Series>
<!-- Line series with point selection enabled -->
<charts:LineSeries ShowPoints="True" SelectionMode="SinglePoint" />
</charts:Chart.Series>
<charts:Chart.XAxis>
<charts:DateTimeAxis Title="Date"/>
</charts:Chart.XAxis>
<charts:Chart.YAxis>
<charts:LinearAxis Title="Value"/>
</charts:Chart.YAxis>
</charts:Chart>
Assigning a title to a series in the chart is done by setting the Title property on its data series. Since in the example we're creating the DataSeries in code-behind, we need to set this property there:
var dataSeries = new DataSeries<DateTime, double>() { Title = "Random Values" };
After these modifications our chart looks like the following:
Styling the Title and Positioning of the Legend
There are still a few issues with the chart: the default title style is too small and the legend is taking too much space on the right of the chart.
We can change the font size of the title text by setting theTitleStyleproperty on Chart. This style needs to have a TargetType of TextBlock.
The legend position can also be set to have it appear inside the plot area, leaving more space for the data. We can do this by setting theLegendPositiononChart. Also, by default the background of the legend is transparent. When displaying it within the plot area, it might look better to change this background to a brush by assigning a style to theLegendStyleproperty:
<UserControl.Resources>
<Style TargetType="charts:Legend" x:Key="CustomLegendStyle">
<Setter Property="Background" Value="{StaticResource PhoneBackgroundBrush}"/>
</Style>
<Style TargetType="TextBlock" x:Key="CustomTitleStyle">
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeExtraLarge}"/>
</Style>
</UserControl.Resources>
(...)
<charts:Chart x:Name="MyChart" Width="700" Height="400" Title="My First Chart"
TitleStyle="{StaticResource CustomTitleStyle}" LegendPosition="RightInside" LegendStyle="{StaticResource CustomLegendStyle}">
<charts:Chart.Series>
<!-- Line series with point selection enabled -->
<charts:LineSeries ShowPoints="True" SelectionMode="SinglePoint" />
</charts:Chart.Series>
<charts:Chart.XAxis>
<charts:DateTimeAxis Title="Date"/>
</charts:Chart.XAxis>
<charts:Chart.YAxis>
<charts:LinearAxis Title="Value"/>
</charts:Chart.YAxis>
</charts:Chart>
The modified chart now looks like the following:
Bar Series and Column Series
So far we've used line series to display data on the chart. Drawing bar or column series is also a frequent requirement and doing so is not very different from working with LineSeries: only the series type needs to be changed to ColumnSeries or BarSeries - and in the case of BarSeries the underlying data source might have to be changed as the axes get different meanings.
Creating a ColumnSeries Using Data Binding
Using column series follows the same process as working with line series, we only need to assign ColumnSeries instances to theSeriescollection on Chart. The data for the series can either be created manually or with binding to a collection of model items. We've seen an example of creating data manually, so let's see an example of creating data via bindings.
First, let's define our model object. In this case the models will be definitions of GDP data:
public class GDPDataPoint
{
public int Year { get; private set; }
public double GDP { get; private set; }
public GDPDataPoint(int year, double gdp)
{
Year = year;
GDP = gdp;
}
}
To set up data binding, we need to create and assign a BindableDataSeries class as theDataSeries property of the series. On thisBindableDataSeries we need to set up the X and Y value bindings and set itsItemsSource property. Let's define the series and theirBindableDataSeries's in XAML and set their ItemsSources in code behind:
<UserControl.Resources>
<Style TargetType="charts:Legend" x:Key="CustomLegendStyle">
<Setter Property="Background" Value="{StaticResource PhoneBackgroundBrush}"/>
</Style>
<Style TargetType="TextBlock" x:Key="CustomTitleStyle">
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeExtraLarge}"/>
</Style>
</UserControl.Resources>
(...)
<charts:Chart x:Name="Chart" LegendPosition="RightInside" Height="440" Width="700" Title="G7 Gross Domestic Product Change"
LegendStyle="{StaticResource CustomLegendStyle}" TitleStyle="{StaticResource CustomTitleStyle}">
<charts:Chart.Behaviour>
<charts:ZoomAndPanGestureBehaviour/>
</charts:Chart.Behaviour>
<charts:Chart.XAxis>
<charts:CategoryAxis Title="Year"/>
</charts:Chart.XAxis>
<charts:Chart.YAxis>
<charts:LinearAxis Title="USD in Billions"/>
</charts:Chart.YAxis>
<!-- Define all 5 series on the chart -->
<charts:Chart.Series>
<charts:ColumnSeries>
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="USA" XValueBinding="{Binding Year}" YValueBinding="{Binding GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries>
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="Japan" XValueBinding="{Binding Year}" YValueBinding="{Binding GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries>
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="Germany" XValueBinding="{Binding Year}" YValueBinding="{Binding GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries>
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="UK" XValueBinding="{Binding Year}" YValueBinding="{Binding GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
<charts:ColumnSeries>
<charts:ColumnSeries.DataSeries>
<charts:BindableDataSeries Title="France" XValueBinding="{Binding Year}" YValueBinding="{Binding GDP}"/>
</charts:ColumnSeries.DataSeries>
</charts:ColumnSeries>
</charts:Chart.Series>
</charts:Chart>
private List<GDPDataPoint> _GDPGermany;
private List<GDPDataPoint> _GDPFrance;
private List<GDPDataPoint> _GDPUK;
private List<GDPDataPoint> _GDPJapan;
private List<GDPDataPoint> _GDPUSA;
public ColumnSeries()
{
InitializeComponent();
// Initalize _GDPUSA, _GDPJapan, _GDPGermany, _GDPUK, _GDPFrance members
InitGdpData();
(Chart.Series[0].DataSeries as BindableDataSeries).ItemsSource = _GDPUSA;
(Chart.Series[1].DataSeries as BindableDataSeries).ItemsSource = _GDPJapan;
(Chart.Series[2].DataSeries as BindableDataSeries).ItemsSource = _GDPGermany;
(Chart.Series[3].DataSeries as BindableDataSeries).ItemsSource = _GDPUK;
(Chart.Series[4].DataSeries as BindableDataSeries).ItemsSource = _GDPFrance;
}
After implementing theInitGdpDatamethod, the deployed example looks like the following on the device:
Creating a BarSeries
BarSeries is basically a ColumnSeries rotated 90 degrees clockwise. However, because of this, it's "logical" X axis is the Y axis, whereas it's Y axis is the X axis of the chart.
Creating a BarSeries is done the very same way a ColumnSeries is and assigning data is done the same way as well (by assigning a DataSeriesor a BindableDataSeries to its DataSeries property). The only difference is that the X and Y values need to be switched from the ColumnSeries for them to make sense.
So to implement the same example as we did on the ColumnSeries we'll have to set the chart's XAxis to be a LinearAxis, the Y axis to be a CategoryAxis and switch the XValueBinding and YValueBinding values on the BindableDataSeries:
<charts:Chart x:Name="Chart" LegendPosition="RightInside" LegendVerticalAlignment="Bottom" Height="440" Width="700" Title="G7 Gross Domestic Product Change"
LegendStyle="{StaticResource CustomLegendStyle}" TitleStyle="{StaticResource CustomTitleStyle}">
<charts:Chart.Behaviour>
<charts:ZoomAndPanGestureBehaviour/>
</charts:Chart.Behaviour>
<!-- X axis is the LinearAxis, while Y is the CategoryAxis when using BarSeries-->
<charts:Chart.XAxis>
<charts:LinearAxis Title="USD in Billions"/>
</charts:Chart.XAxis>
<charts:Chart.YAxis>
<charts:CategoryAxis Title="Year"/>
</charts:Chart.YAxis>
<!-- XValueBinding and YValueBinding are the inverse of when working with ColumnSeries -->
<charts:Chart.Series>
<charts:BarSeries>
<charts:BarSeries.DataSeries>
<charts:BindableDataSeries Title="USA" XValueBinding="{Binding GDP}" YValueBinding="{Binding Year}"/>
</charts:BarSeries.DataSeries>
</charts:BarSeries>
<charts:BarSeries>
<charts:BarSeries.DataSeries>
<charts:BindableDataSeries Title="Japan" XValueBinding="{Binding GDP}" YValueBinding="{Binding Year}"/>
</charts:BarSeries.DataSeries>
</charts:BarSeries>
<charts:BarSeries>
<charts:BarSeries.DataSeries>
<charts:BindableDataSeries Title="Germany" XValueBinding="{Binding GDP}" YValueBinding="{Binding Year}"/>
</charts:BarSeries.DataSeries>
</charts:BarSeries>
<charts:BarSeries>
<charts:BarSeries.DataSeries>
<charts:BindableDataSeries Title="UK" XValueBinding="{Binding GDP}" YValueBinding="{Binding Year}"/>
</charts:BarSeries.DataSeries>
</charts:BarSeries>
<charts:BarSeries>
<charts:BarSeries.DataSeries>
<charts:BindableDataSeries Title="France" XValueBinding="{Binding GDP}" YValueBinding="{Binding Year}"/>
</charts:BarSeries.DataSeries>
</charts:BarSeries>
</charts:Chart.Series>
</charts:Chart>
The example looks the following when deployed to the phone:
Download the Source Code
The source code for all the above examples can be downloaded from here: Visiblox - Basic Charts for Windows Phone 7.zip. To compile the examples, you'll need to download the latest free version of Visiblox Charts for WP7 and add the WP7 dll as reference to the project. You'll also need the Windows Phone 7 Developer Tools installed.