Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Grouping with StackedColumnSeries Charts

0.00/5 (No votes)
8 Aug 2011 1  
Adding support for groups to the Silverlight Toolkit's StackedColumnSeries chart.

Demo Chart

Introduction

I have been building many dashboards for clients, and recently upgraded to Silverlight 4 and its corresponding Toolkit version. I went straight to the stacking features of the charts, and found a very surprising thing. If you add more than one StackedColumnSeries, only one column would show.

The reason this is surprising is that the non-stacking version of the ColumnSeries does group nicely.

ColumnSeries

Since grouping (or clustering as Excel would say) stacking columns together is very useful in comparing groups of information against each other without losing granularity, I decided to add this missing feature. It turned out to be an extremely simple change, but provides a sleek upgrade.

Charting Architecture

A Chart is made up of one or more series. Normally, each series would be a single line or column etc. The design of the charting system is very flexible, so a custom series can basically be anything it wants to be. In order to be this flexible, the chart looks at the series definitions to help determine the axis details and range. Then it goes through the defined series and has them draw themselves.

Like all controls placed in the same container, they are drawn in the order they get declared. So when there are multiple series that want to occupy the same screen area, only the last one can be seen.

The stacked series framework adds a new layer of abstraction to the setup. Instead of just adding a ColumnSeries into the chart, you add a StackedColumnSeries. Into that, you define the actual series that gets stacked together in each category.

This StackedColumnSeries then manages all the defined series and DataItems and configures where they are drawn. And since its implementation stretches each column across the entire width assigned to each category, if there are multiple declared, only one will be seen.

The Upgrade

Since the StackedColumnSeries class handles the positioning of DataItems, I inherited from it, and overrode the UpdateDataItemPlacement method. I basically copied this method and the required private and internal dependencies from the Toolkit's source code. Then updated the position and width each DataItem was assigned.

The first part is to find out how many groups there were going to be and which Series was being configured.

var numSeries = this.SeriesHost.Series.Count;
int index = this.SeriesHost.Series.IndexOf(this);

Then update the rendering details based on that:

width = width / numSeries;
leftCoordinate = leftCoordinate + (width * index);

Using the Code

With my upgrade, all you have to do is define more than one GroupedStackedColumnSeries in the chart. Every one of these defines a separate column of stacked values.

The setup below creates two sets of stacked columns. Comparing Net (realized and projected) against Gross (realized and projected) for each category.

<chartingToolkit:Chart x:Name="chart" BorderThickness="0" >
    <chartingToolkit:Chart.Series>
        <sdk:GroupedStackedColumnSeries >
            <chartingToolkit:SeriesDefinition Title="Net (Projected)" 
            IndependentValueBinding="{Binding Name}" DependentValueBinding="{Binding Value}" />
            <chartingToolkit:SeriesDefinition Title="Net (Realized)" 
            IndependentValueBinding="{Binding Name}" DependentValueBinding="{Binding Value}" />                               
        <sdk:GroupedStackedColumnSeries >
            <chartingToolkit:SeriesDefinition Title="Gross (Projected)" 
            IndependentValueBinding="{Binding Name}" DependentValueBinding="{Binding Value}"  />
            <chartingToolkit:SeriesDefinition Title="Gross (Realized)" 
            IndependentValueBinding="{Binding Name}" DependentValueBinding="{Binding Value}" />
        </sdk:GroupedStackedColumnSeries>
    </chartingToolkit:Chart.Series>
    <chartingToolkit:Chart.Axes>
        <chartingToolkit:LinearAxis Orientation="Y" 
             Minimum="0" Location="Left" Title="Sales"  />
        <chartingToolkit:CategoryAxis Title="Category" Orientation="X"/>
    </chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>

Data is loaded by filling all the SeriesDefinitions' ItemSources. I think of the required data structure to be an array of Key / Value pairs, one entry for each category item or layer in that column stack.

StackedColumnSeries series = chart.Series[0] as StackedColumnSeries;
series.SeriesDefinitions[0].ItemsSource = e.Result.Result.RealizedNet;
series.SeriesDefinitions[1].ItemsSource = e.Result.Result.ProjectedNet;
     
series = chart.Series[1] as StackedColumnSeries;
series.SeriesDefinitions[0].ItemsSource = e.Result.Result.RealizedGross;
series.SeriesDefinitions[1].ItemsSource = e.Result.Result.ProjectedGross;

Points of Interest

I added in some selection logic as well, to ensure only one DataPoint from any series can be selected at once, even if they are in separate columns. This was just to improve the user experience of the groups.

**Don't set backgrounds - since series layer on top of each other, the last one with a background will hide all the previous ones.

History

  • Aug. 6, 2011 - Original article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here