Blog

Visiblox for WP7 - Basic Charts, Selection and Zooming

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. To add zooming and panning we need to set the Chart's Behaviour to ZoomAndPanGestureBehaviour 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:ZoomAndPanGestureBehaviour/>
    </charts:Chart.Behaviour>
</charts:Chart>

Having added this, the chart can now be zoomed in by pinching, zoomed out by double tapping and 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.

Comments

Hi

I was wondering whether the WP7 library also works for Windows Phone 7.1, i.e., the Mango release? I tried to run the example project on the emulator that comes with the Windows Phone SDK 7.1 Beta 2. But I did not succeed. Navigating to any of the three demo charts results in an "InvalidProgramException" when the respective view is being initialized.

Is there anything I can do, to fix that issue? Or is it the library that needs to be fixed?

Thanks.

 
Posted by Sandro

Sandro,

The chart doesn't yet officially support WP 7.1, but we are aware of the issue you're referring to and are working on a fix. I'll be sure to let you know when that fix is available.

Regards

Jesse

 
Posted by Jesse Beaumont

Is the Visiblox – Basic Charts for Windows Phone 7.zip. available? I am not able to download,...

 
Posted by Arantxa

Arantxa,

The link was broken but is now working. Thanks for pointing it out!

 
Posted by Effrosyni Kouri

Sandro, there is a KB article that addresses your issue which I cannot find at the moment but there is an easy fix. In the ctor for your App include the following code:

// Dummy ctor call to fix the // ‘knownTypes’ argument was invalid because it was null. All known types specified must be non-null values. new DataSeries();

it should work after this change.

 
Posted by Ken

Hi, I downlaod the dll for WP7. Referencing it in project as documented. Using vb.net with Wp7 development. When I compile, I get an Invalid Operation: watermark must be on...." In the designer, the watermark is on the chart...I do not even know how to remove it. What is this?

Do also have some vb.net examples of how to create a simple line series. I would like a late binding example (chart.series(0).seriesdata.add.......)

Regards,

Theo

 
Posted by Theo

Hi Theo, what you're seeing is a known issue with Visiblox Charts on the Mango release of Windows Phone 7 (which I assume you're using)? This issue is being fixed in the next release which is going to be out in a week or two. In the meantime, it should work fine on 7.0.

Regards,

Jesse

 
Posted by Jesse Beaumont

Hi Jesse,

Ok, thanks. I got it working (if you are quick and click on the OK buttun of the errors on the phone, it does continue on the phone, but not in vs).

Now, if I may (all vb.net please): - How can I set the size of the axis labels (or where can I read about it) - Make grid line lighter color/change transparency (or where can I read about it) - Gif the graph an end frame (or where can I read about it)

Possitive: - Nice and fast (sad I had to strugle in the beginning...I almost gave up on it) - Does what is needed for a phone chart

Thanks,

Theo

 
Posted by Theo

Hello, may I know if visiblox is able to define the length of the chart? Is visiblox able to adding on to the chart, if the data input into it is indefinite? Let's say, I want to keep data for up to a year only, how do I do this?

 
Posted by Boh Zhuang Hao

There is no automatic way of doing this but data in Visiblox is defined by a IDataSeries and it observes any CollectionChanged events raised, so if you add data to the end of the data series, this will be added to the end of the chart and similarly if you remove data from the start of the data series that will be removed from the chart. So one solution would be to check the first point and last point in your data series every time you add a data point and if the time span between them is greater than your maximum, remove the first point.

A second, more sophisticated solution would be to implement a FilterStrategy for the FilteredDataSeries to only expose the last years worth of data to the chart which would then happen automatically.

Jesse

 
Posted by Jesse Beaumont

Hello, I have a question. Are the points on the lineseries dynamically changeable? Let's say I have 5 points on the lineseries, namely January through May. I have some statistics that needs to be calculated and displayed on the graph on a day-by-day basis. As such, The data for January will keep changing until February comes, where by then, I will stop altering January's statistics and start on February's.

 
Posted by Boh Zhuang Hao

Yes the chart will respond to changes in the data. It subscribes to CollectionChanged and PropertyChanged events so it will update when you add or remove points as well as when you modify or replace existing points as long as your IDataSeries and IDataPoint implementations correctly implement INotifyCollectionChanged and INotifyPropertyChanged respectively. All the "out-of-the-box" implementations of those interfaces support those event types.

Jesse

 
Posted by Jesse Beaumont

Hello, I'm currently using Silverlight Toolit for WP7 from November 2011. But this version doesnt work with currently downloadable version of Visiblox for WP7. An exception is thrown within InitializeComponent of the page which contains the chart (InvalidProgramException: Unknown parser error: Scanner 2148474880. [Line: 32 Position: 8]). Any informations when a new build will be available? However, thanks a lot for this charting control!

 
Posted by schue

The next version of Visiblox Charts (version 2.2) will support the November release of the toolkit. The new release is due out very soon (the next few days).

Thanks,

Antony

 
Posted by Antony Welsh

Post a comment