The blog of dlaa.me

Simple column labels you can create at home! [Re-Templating the Silverlight/WPF Data Visualization ColumnDataPoint to add annotations]

A customer contacted me over the weekend asking how to add labels (also known as annotations) to a ColumnSeries. My reply was that we don't support annotations in Silverlight/WPF Charting yet, but it's possible to create some pretty simple ones for limited scenarios just by editing the default ColumnDataPoint Template. And because it's so quick, I thought I'd write up a brief example on the bus!

Aside: For some more examples of basic DataPoint Template changes, please have a look at my earlier post on customizing ToolTips.

 

In this case, the customer wanted to add a label showing the column's value at the bottom of the column, just above its axis label. (This is a nice place to put labels because it makes it easy for viewers to associate the category with its value no matter how high each column is.) The obvious approach is to add a TextBlock to the body of the default Template, but the problem with that is that the text can get clipped or even disappear for small columns... So the trick is to add a negative Margin to pull the text "outside" the normal clipping region. Fortune must have been smiling upon me, because when I tried this on Silverlight, it worked just like I wanted! :)

Aside: My other idea was to use a Canvas because it doesn't clip by default; maybe someone else will need to use that approach for their scenario.

 

Here's how the resulting chart looks:

Simple column annotations (on bottom)

The XAML's nothing special - aside from the negative Margin, it's all standard stuff:

<charting:Chart
    Title="Simple Column Annotations - Bottom">
    <charting:ColumnSeries
        DependentValuePath="Value"
        IndependentValuePath="Key"
        ItemsSource="{Binding}">
        <charting:ColumnSeries.DataPointStyle>
            <Style TargetType="charting:ColumnDataPoint">
                <Setter Property="Background" Value="Yellow"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="charting:ColumnDataPoint">
                            <Grid>
                                <Rectangle
                                    Fill="{TemplateBinding Background}"
                                    Stroke="Black"/>
                                <Grid
                                    Background="#aaffffff"
                                    Margin="0 -20 0 0"
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Bottom">
                                    <TextBlock
                                        Text="{TemplateBinding FormattedDependentValue}"
                                        FontWeight="Bold"
                                        Margin="2"/>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </charting:ColumnSeries.DataPointStyle>
    </charting:ColumnSeries>
</charting:Chart>

 

Just for fun, I thought I'd try the same trick to put the annotations on top of the columns, too. This is the more traditional location - and that also works pretty nicely:

Simple column annotations (on top)

The only change from the previous XAML is switching the VerticalAlignment to Top:

<Grid
    Background="#aaffffff"
    Margin="0 -20 0 0"
    HorizontalAlignment="Center"
    VerticalAlignment="Top">
    <TextBlock
        Text="{TemplateBinding FormattedDependentValue}"
        FontWeight="Bold"
        Margin="2"/>
</Grid>

 

And there you have it - a simple technique for simple column annotations!

Aside: Of course, these aren't "real" annotations - they'll eventually break in more complicated scenarios. But hey, if you've got simple needs, here's a simple solution for you. :)

 

PS - For people playing along at home, here's how I created the data for the samples:

public MainPage()
{
    InitializeComponent();
    var items = new List<KeyValuePair<string, double>>();
    items.Add(new KeyValuePair<string,double>("Apples", 0));
    items.Add(new KeyValuePair<string,double>("Oranges", 0.1));
    items.Add(new KeyValuePair<string,double>("Pears", 1));
    DataContext = items;
}