Delay's Blog

Silverlight, WPF, Windows Phone, .NET, and more...

Turn your head and check out this post [How to: Easily rotate the axis labels of a Silverlight/WPF Toolkit chart]

Turn your head and check out this post [How to: Easily rotate the axis labels of a Silverlight/WPF Toolkit chart]

  • Comments 11

When someone asked me how to rotate the axis labels of a chart from the Data Visualization package of the Silverlight Toolkit/WPF Toolkit earlier today, I realized it was time for a quick blog post. Because when I've answered a question two or three times, it's usually a pretty good sign that I'll keep on answering it for some time. I usually try to head that kind of thing off at the pass, so here's my post on the topic for the benefit of future generations. :)

The typical scenario here is that someone has a chart and it's working well, but their axis labels are very long and end up overlapping - even after the default axis behavior of putting them in alternating rows to prevent such a problem kicks in:

Overlapping axis labels

 

The typical solution is to rotate the axis labels - and it's easy once you know where to look. The key here is to customize the Template of the AxisLabel instances that are used to render the labels. And it's quite simple to do so by providing a Style with a Template Setter for the AxisLabelStyle property of the Axis subclass in question:

Rotated axis labels on WPF

Yeah, it looks great on paper; but that description was a mouthful...

It's probably easier to understand in XAML - here's the complete code for the sample above with the interesting part highlighted:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:spec="clr-namespace:System.Collections.Specialized;assembly=System"
        Title="RotatedAxisLabelsWPF"
        Width="500"
        Height="350">
    <Grid>
        <charting:Chart
            Title="Animals With Long Names">
            <charting:ColumnSeries
                Title="Character count"
                DependentValueBinding="{Binding Length}"
                IndependentValueBinding="{Binding}">
                <charting:ColumnSeries.ItemsSource>
                    <spec:StringCollection>
                        <sys:String>Bumblebee</sys:String>
                        <sys:String>Caterpillar</sys:String>
                        <sys:String>Hippopotamus</sys:String>
                        <sys:String>Rhinoceros</sys:String>
                        <sys:String>Velociraptor</sys:String>
                    </spec:StringCollection>
                </charting:ColumnSeries.ItemsSource>
                <charting:ColumnSeries.IndependentAxis>
                    <charting:CategoryAxis
                        Orientation="X">
                        <charting:CategoryAxis.AxisLabelStyle>
                            <Style TargetType="charting:AxisLabel">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="charting:AxisLabel">
                                            <TextBlock Text="{TemplateBinding FormattedContent}">
                                                <TextBlock.LayoutTransform>
                                                    <RotateTransform Angle="-60"/>
                                                </TextBlock.LayoutTransform>
                                            </TextBlock>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </charting:CategoryAxis.AxisLabelStyle>
                    </charting:CategoryAxis>
                </charting:ColumnSeries.IndependentAxis>
            </charting:ColumnSeries>
        </charting:Chart>
    </Grid>
</Window>

Like I said, it's all pretty standard stuff once you know where to look. Of course, you can rotate the labels all the way to 90 degrees if you want them to take the least amount of space possible. But 60 degrees seemed like a suitably rakish angle. ;)

 

Unfortunately, we can't declare "Mission Accomplished" quite yet... While the Data Visualization assembly itself works exactly the same on WPF and Silverlight, the platforms themselves aren't identical quite yet. Specifically, there's no support for LayoutTransform in Silverlight (and RenderTransform is simply not appropriate here). Fortunately, I've filled the LayoutTransform gap with my LayoutTransformer class - and it's already part of the Silverlight Toolkit!

The syntax changes just a bit, but the concept is exactly the same:

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    xmlns:layout="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit">
    <Grid>
        <charting:Chart
            Title="Animals With Long Names">
            <charting:ColumnSeries
                Title="Character count"
                DependentValueBinding="{Binding Length}"
                IndependentValueBinding="{Binding}">
                <charting:ColumnSeries.ItemsSource>
                    <toolkit:ObjectCollection>
                        <sys:String>Bumblebee</sys:String>
                        <sys:String>Caterpillar</sys:String>
                        <sys:String>Hippopotamus</sys:String>
                        <sys:String>Rhinoceros</sys:String>
                        <sys:String>Velociraptor</sys:String>
                    </toolkit:ObjectCollection>
                </charting:ColumnSeries.ItemsSource>
                <charting:ColumnSeries.IndependentAxis>
                    <charting:CategoryAxis
                        Orientation="X">
                        <charting:CategoryAxis.AxisLabelStyle>
                            <Style TargetType="charting:AxisLabel">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="charting:AxisLabel">
                                            <layout:LayoutTransformer>
                                                <layout:LayoutTransformer.LayoutTransform>
                                                    <RotateTransform Angle="-60"/>
                                                </layout:LayoutTransformer.LayoutTransform>
                                                <TextBlock Text="{TemplateBinding FormattedContent}"/>
                                            </layout:LayoutTransformer>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </charting:CategoryAxis.AxisLabelStyle>
                    </charting:CategoryAxis>
                </charting:ColumnSeries.IndependentAxis>
            </charting:ColumnSeries>
        </charting:Chart>
    </Grid>
</UserControl>

Mission accomplished:

Rotated axis labels on Silverlight

 

There you have it: AxisLabelStyle is your new best friend. A friend with benefits, one might say, because there are other cool things you can do by customizing the AxisLabel Style.

So please: go forth and enjoy your new friend!

  • What are you doing? Do you want to confuse me more? Try a more simple code so we illiterate can understand and use it, eventually.

  • sidney26stevens,

    Thanks for the feedback - though I'm not sure I fully understand what you're asking for. :( Are you suggesting that I should have excerpted away more of the XAML?

    When I wrote this post (admittedly on fairly little sleep!), it seemed nice to have the complete code all there in one place for the taking - and to set everything in full context.

    If you have suggestions for making this sample easier to understand, I'd be happy to hear them. You can contact me via the "Email" link at the top of the sidebar on the right if you'd like.

    Thanks!

  • Delay,

    Thanks for this, it helped it is a very useful example.

    I have an issue when this is used with two column series though; if you put the same code on both ColumnSeries you only get one series, or more correctly one series overlayed on another; they no longer appear side by side.

    I have considered using a template for all column series in the chart but that does not seem to resolve the issue either.

    I am happy to provide a code example if you need further explanation.

    Thanks

  • gregsy,

    If you want two ColumnSeries instances to share the same Axis instance, you want to put that Axis in the Chart.Axes collection instead of defining it on each ColumnSeries separately (which I did above to be explicit). I'm not sure this is what you're seeing, but it sounds a lot like it - please let me know if making that change doesn't solve your problem.

    Thanks!

  • Thanks Delay,

    When I finally got round to testing it it works a treat.

    For those interested

    Assuming I have a class for Price containing a definition :

     double LitrePrice

     string Date

    The following code

    <Grid>

           <charting:Chart>

               <charting:ColumnSeries

                   Title="Petrol (Litre)"

                   DependentValueBinding="{Binding LitrePrice}"

                   IndependentValueBinding="{Binding Date}"

                   ItemsSource="{StaticResource PetrolCollection}">

               </charting:ColumnSeries>

               <charting:ColumnSeries

                   Title="Diesel (Litre)"

                   DependentValueBinding="{Binding LitrePrice}"

                   IndependentValueBinding="{Binding Date}"

                   ItemsSource="{StaticResource DieselCollection}">

               </charting:ColumnSeries>

               <charting:Chart.Axes>

                   <charting:CategoryAxis Orientation="X">

                       <charting:CategoryAxis.AxisLabelStyle>

                           <Style TargetType="charting:AxisLabel">

                               <Setter Property="Template">

                                   <Setter.Value>

                                       <ControlTemplate TargetType="charting:AxisLabel">

                                           <TextBlock Text="{TemplateBinding FormattedContent}">

                                                   <TextBlock.LayoutTransform>

                                                       <RotateTransform Angle="300"/>

                                                   </TextBlock.LayoutTransform>

                                           </TextBlock>

                                       </ControlTemplate>

                                   </Setter.Value>

                               </Setter>

                           </Style>

                       </charting:CategoryAxis.AxisLabelStyle>

                   </charting:CategoryAxis>

               </charting:Chart.Axes>

           </charting:Chart>    

       </Grid>

    Gives a two series chart with rotated axes.

  • Thanks for your post. It helped me a lot. I got your sample working but I now need to create my series for the chart dynamically at runtime depending on user input. Is there a way to do the rotattion in code on a newly created series?

    Another question where you could possible help: When creating my series dynamically the colors are randomly choosen each time for a new series. I need them to be fixed so every time a users sees the same chart with the same entries they should have the same color. I found some old posts that referenced a method that could reset the color selection routine somehow but this was removed from silverlight toolkit ...

    I hope you have some advice ...

    Anyways thanks for your stuff!

  • bhb,

    Anything that can be done in XAML can be done in code. :) It should be relatively straightforward to translate the XAML above into code so that you can use it in your code-only scenario. (While I haven't done so for this example specifically, I have done so for similar examples in the past.)

    Regarding the creation of constant-color Series, the trick is to use the DataPointStyle property to assign a Style directly to each Series you care about that sets the Background property to the Brush you want to be associated with that series. I discuss this in the following post which contains an example of this very thing: blogs.msdn.com/.../chart-tweaking-made-easy-how-to-make-four-simple-color-tooltip-changes-with-silverlight-wpf-charting.aspx

    Hope this helps!

  • Hello,

    Is there any style that can shrink or reduce the space between series on a bar graph, so that the bars appear closer together despite being different series?

    Thank you.

  • Moni,

    I don't believe there's a way to do that today without subclassing BarSeries. FYI that we have a work item to consider adding a "columns/bars should take X% of the slot size" property that would allow people to change the current default of X=80 - this might eventually let you do what you'd like quite easily.

  • I'm using Silverlight 3, and it claims that LayoutTransform isn't a valid property for the TextBlock control.

    Am I not doing something?  

    Beyond that, it would be handy if you could start posting c# code fas well as the XAML for creating a style like this.

  • john simmons,

    If you have a closer look at the post, there's one sample for WPF (where the LayoutTransform property exists) and another sample for Silverlight (where it doesn't and my LayoutTransformer class must be used to simulate it). The error you're getting sounds like you're trying to use the WPF XAML on Silverlight. If you switch to the Silverlight sample and add a reference to the System.Windows.Controls.Layout.Toolkit assembly, you should be set. :)

    Regarding C# vs. XAML, I tend to only post a single way of doing things - and in cases where a XAML-only solution is possible (like this one), I tend to prefer that form. For UI issues, I feel that XAML is a more appropriate language to express things - however, it's all just code and it's usually fairly straightforward to translate XAML into the corresponding code.

    If I had all the time in the world, I might translate my samples into more forms - but because I don't, I usually try to pick the one form that I feel is demonstrates the "best practice". I hope that's helpful!

Page 1 of 1 (11 items)
Leave a Comment
  • Please add 3 and 6 and type the answer here:
  • Post