The blog of dlaa.me

Please rate your dining experience [How to: Show text labels on a numeric axis with Silverlight/WPF Toolkit Charting]

A customer contacted me a few days ago asking how to display text labels on a NumericAxis. (Whereas CategoryAxis makes it easy to use text labels for the independent axis, this request was about text labels on the dependent axis.) It's a bit of an unusual request (I spent a minute just now and don't see how to accomplish this in Excel), but I knew it would be easy to do with the Charting controls in the Data Visualization assembly that's part of the Silverlight Toolkit and WPF Toolkit.

The underlying scenario is to provide labels for the results of one of those "How are we doing?" surveys restaurants and hotels like to give out. The chart should look like this:

Text labels on the dependent axis

Though I originally suggested a different approach, the act of coding it up myself suggested a trusty old IValueConverter would be most appropriate. (Aside: See more IValueConverter tricks here and here.) The basic approach is to explicitly specify the dependent axis and then use it to configure exactly the set of tick marks we want. Once that's done, a simple bit of IValueConverter magic converts the numeric values into their corresponding text labels - and the problem is solved! :)

 

I've added the sample shown here to my DataVisualizationDemos application which is collection of all the Data Visualization samples I've blogged. Like the core Data Visualization code itself, the demo app compiles for and runs on multiple platforms with the same code and XAML - it's an easy way to publish a sample and show it running on Silverlight 3, Silverlight 4, WPF 3.5, and WPF 4. Just for kicks, I've used the "Compatible" ColumnSeries for this example - but it works just as well with traditional ColumnSeries. Better yet, the basic idea can be generalized to solve a variety of similar problems as well!

 

[Click here to download the complete source code for the cross-platform DataVisualizationDemos sample application. ]

 

Here's the relevant XAML:

<!-- Chart of customer feedback -->
<charting:Chart Title="Customer Feedback">
    <compatible:ColumnSeries
        ItemsSource="{Binding}"
        IndependentValuePath="Topic"
        DependentValuePath="Rating">

        <!-- Custom Y axis for text labels -->
        <compatible:ColumnSeries.DependentRangeAxis>
            <charting:LinearAxis
                Orientation="Y"
                Minimum="0"
                Maximum="4"
                Interval="1"
                ShowGridLines="True">

                <!-- Custom style/template for text labels -->
                <charting:LinearAxis.AxisLabelStyle>
                    <Style TargetType="charting:AxisLabel">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="charting:AxisLabel">
                                    <TextBlock Text="{Binding Converter={StaticResource RatingToStringConverter}}"/>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </charting:LinearAxis.AxisLabelStyle>
            </charting:LinearAxis>
        </compatible:ColumnSeries.DependentRangeAxis>

        <!-- Custom style for different background -->
        <compatible:ColumnSeries.DataPointStyle>
            <Style TargetType="charting:DataPoint">
                <Setter Property="Background" Value="#ff00a0e0"/>
            </Style>
        </compatible:ColumnSeries.DataPointStyle>
    </compatible:ColumnSeries>
</charting:Chart>

And code:

/// <summary>
/// Implements IValueConverter to convert from double rating values to friendly string names.
/// </summary>
public class RatingToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Validate parameters
        if (!(value is double))
        {
            throw new NotSupportedException("Unsupported value type in RatingToStringConverter.");
        }
        // Convert number to string
        double doubleValue = Math.Floor((double)value);
        if (0.0 == doubleValue)
        {
            return "Awful";
        }
        else if (1.0 == doubleValue)
        {
            return "Poor";
        }
        else if (2.0 == doubleValue)
        {
            return "Fair";
        }
        else if (3.0 == doubleValue)
        {
            return "Good";
        }
        else if (4.0 == doubleValue)
        {
            return "Great";
        }
        else
        {
            throw new ArgumentException("Unsupported value in RatingToStringConverter.");
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}