The blog of dlaa.me

Silverlight Charting is faster and better than ever [Silverlight Toolkit March 09 release now available!]

We've just published the March 09 release of the Silverlight Toolkit and I bet there's something in there to make just about everyone happy! There are bug fixes for some of your favorite controls, a variety of cool new features sprinkled all around, and some completely new controls that will add a little more excitement to your next Silverlight application. The Toolkit continues to support everyone's ongoing Silverlight 2 development efforts and now also supports the newly released Silverlight 3 Beta. What's more, you get all of this goodness for the same low, low price of: free! :)

I encourage everyone to have a look at the live samples for Silverlight 2 or Silverlight 3, download the Toolkit installer(s), and enjoy the new stuff!

Aside: As a public service, I'll also direct your attention to the new LayoutTransformer control which didn't get its own sample page this time around, but remains near and dear to my heart. And if you look closely enough, you just might be able to spot LayoutTransformer being used by Accordion and Charting...

 

But enough about the rest of the Toolkit; you're probably here because you want to learn more about Silverlight Charting. Well, you've come to the right place! If you haven't already done so, this might be a good time to read or review my previous two introductory posts for Charting: this one for the initial November release and this one for the follow-up release in December. The March 09 release of Charting builds on the foundation Jafar and I previously established to add some new features, improve some usability scenarios, fix some bugs, and address what was easily our most common request: better performance!

Let's start with the full version of the Charting release notes (a simplified version of these notes is available on the "What's New" page for the Toolkit - but it omits all the juicy details):

Notable Changes

Significant performance improvements have been made to many parts of the Charting framework which yield noticeable speed-ups for scenarios dealing with large data sets. Because performance is one of those areas where it's easy to make improvements against benchmarks and yet achieve very little benefit that's relevant to the user, I won't cite the benchmark-based improvements our testing shows for this release. Instead, I'll share the anecdotal story of a Charting customer who got a chance to try an early version of the code and found that a scenario which was starting to bog down with around 500 points under the previous release could now manage over 5000 points with no difficulty! Your mileage may vary, of course, but we definitely think the new Charting core feels snappier!

Added new Series type AreaSeries which works just like LineSeries except that it fills in the area underneath the line. With the addition of AreaSeries in this release, Silverlight Charting now supports all of the major chart types offered by the top level of Excel's ribbon! We've still got some work to do before we can think about trying to replace Excel (like supporting stacked series!), but it's nice to be moving in the right direction and to have achieved this "mini milestone". :)

Any Series can use a CategoryAxis as its independent value axis. Specifically, it is now possible to create a LineSeries with values on the vertical axis and categories (ex: Dogs, Cats, Fish) on the horizontal axis. The purists among you might argue that it's inappropriate to use a LineSeries in this manner because the presence of a line connecting each value suggests a cross-value correlation that's typically not present with categorized data. So the purists are welcome to ignore this feature entirely - but those of you who have requested this feature should please go ahead and make use of it however you feel best fits your particular scenario. :)

Added DependentValuePath and IndependentValuePath string-typed properties to the Series classes (and a corresponding SizeValuePath property to BubbleSeries) to significantly simplify the experience of binding to properties of the underlying data objects being visualized. Instead of having to type something like DependentValueBinding="{Binding Value}", it's now also possible to use the simpler, easier to read DependentValuePath="Value". In fact, we like the simplicity of the new form so much that we've made the new *Path properties the default user-visible properties at design time! The *Binding properties are still present, of course, and can be used for more advanced scenarios such as those where it's necessary to use the Converter parameter of the Binding. The continued support of the *Binding properties also means that any existing code using the Charting core will continue to work just fine as-is and won't need to be changed.

Numerous design-time enhancements for Silverlight 3 and Blend 3. The most obvious improvement here is that dragging a new Chart onto the design surface automatically creates some simple sample data as well as a ColumnSeries that's properly hooked up to that data. This means that not only does the new chart look like a chart (instead of being mostly blank), but it also demonstrates how to correctly hook a Series up to a data source. It's like getting a mini Charting tutorial for free! :) Another big improvement in this area is better exception handling during design-time so the code is less likely to throw exceptions during the intermediate configuration steps that are common to interactive design. This makes it easier to build a chart interactively without having it complain about missing values along the way. Other design-time improvements include improved metadata and a slightly improved workflow.

Added support for Silverlight 3's new easing feature for better control over how data change animations behave. Available only with Silverlight 3 Charting, the new TransitionEasingFunction property of Series makes it easy to select any of Silverlight 3's easing functions to be used when animating the value change of the data points in a Chart. The default value, QuadraticEase, provides a smoother, more pleasing animation that helps keep the viewer focused on the data itself. And while we haven't explicitly changed the DataPoint default fade in/fade out show/hide transition behavior to use an easing function, it's easy for anyone else to do so by providing a custom DataPoint Template in the usual manner.

Added ExtendRangeToOrigin and Origin properties to LinearAxis to enable more scenarios when this axis is used for dependent values. The ExtendRangeToOrigin property is handy when you know you want to display the origin (0) on the dependent axis, but you don't want to have to set Minimum or Maximum because your data could have positive or negative values - or both! Setting ExtendRangeToOrigin is an easy way to ensure that a ColumnSeries or BarSeries always displays the full extent of its columns/bars which makes it easy for the viewer to draw conclusions based on the relative sizes of the elements. Similarly, the Origin property allows you to override the default origin for scenarios where 0 just isn't good enough.

Changed the underlying types of the Chart's Series and Axis properties to improve the code- and design-time experience when using them. As a result, Blend now automatically filters its class list and offers only valid types when adding a new item to either of these collections.

Fixed a memory leak that could occur when a Chart was removed from the visual tree and had one or more Series with their ItemsSource properties set to an ObservableCollection(T) owned by the application. The specifics of the leak scenario are a bit technical, so I'll refer interested readers to this post of mine which contains a fairly detailed description of the problem and its solution.

Changed the default Template of ScatterDataPoint to improve performance and provide more consistent behavior. The previous ScatterDataPoint Template employed a RotateTransform to create its diamond shape - in this release we've switched to a Path which enables us to simplify the Template reduce the ScatterDataPoint's overhead just a bit.

Various architectural changes to improve the current and future programmability story for Charting. Most of these changes aren't the kinds of things that you'll notice as a user, but they're part of our ongoing effort to open up the Charting platform and provide a solid foundation for others to build upon.

Breaking Changes

Assembly name changed to System.Windows.Controls.DataVisualization.Toolkit.dll as part of a Toolkit-wide assembly renaming task. Because this change wasn't specific to Charting, I won't discuss it here. Please refer to the Toolkit release notes for more information about the renaming and how to easily migrate your applications.

Root namespace changed to System.Windows.Controls.DataVisualization as part of a Toolkit-wide namespace renaming task. As above, this change had wasn't specific to Charting and will not be discussed here.

Switched AxisOrientation enumeration values from Horizontal/Vertical to X/Y for clarity, succinctness, and future-proofing. As part of the architectural work that went on, we realized that we'd eventually be switching to X/Y as we introduced certain other features and decided to make the change now to help ease the transition later.

Some of the *Axis properties on the Series classes have been renamed to be more general as a consequence of adding more flexible axis support to the Series classes. For example, what used to be named IndependentRangeAxis is now simply IndependentAxis.

Data values represented by strings are no longer automatically converted to numbers or dates. This is another consequence of the more flexible axis support: when a chart is taking advantage of automatic axis creation, we can't be sure what the desired behavior is if the values "2007", "2008", "2009" are provided as the independent values. Specifically, it's unclear whether the intent is to create a CategoryAxis with three string categories or to create a LinearAxis spanning those numeric values. So we've resolved the ambiguity by requiring stronger typing of dependent and independent values: numeric values must be a numeric type (int, double, etc.) and chronological values must be a DateTime. Otherwise, category values will be used where possible. It's unfortunate that this change may break some existing scenarios, but we believe the ambiguity it removes is worthwhile and the overall behavior is now more consistent and predictable. Charting has always encouraged you to be in charge of your data and this change simply reinforces that - if you want to visualize numbers, then you need to provide numbers. :)

The default Template of Chart has been reworked slightly to make things a bit easier for designers to customize. The most obvious change is the introduction of a new EdgePanel primitive for sizing/positioning the plot area and axes. Using EdgePanel makes this axis placement process more transparent to the designer and should be a little more resilient than the previous Grid-based approach.

Legend is now a TemplatePart of Chart; its LegendItems property has been removed. Certain architectural changes made it necessary for the Chart to be able to identify its Legend and that meant that we needed to formalize things a bit and make Legend a TemplatePart. There's a slight loss of generality here because it is no longer possible to put two or more Legends in the same Template, but as far as we know, nobody was doing this anyway. Note that it is still perfectly acceptable to omit the Legend entirely; TemplateParts are always optional in a Template.

Removed the MarkerWidth/MarkerHeight properties for LineSeries and ScatterSeries. We use the term "convenience properties" for properties that exist to make things easier for the user but that aren't actually necessary because there are other ways of doing the same thing. During development we're always trying to balance the benefits of convenience properties against the API bloat and possible confusion they can cause. In this case, we decided the MarkerWidth and MarkerHeight properties added very little value over re-Templating LineDataPoint or ScatterDataPoint (which is the default approach for changing the appearance of something in WPF and Silverlight). So the default width and height for these DataPoints are now set by their default Templates - and can be customized by re-Templating in the usual manner.

The DataPointSeries and DataPoint classes have been changed to internal visibility to indicate that we are not yet ready for people to be deriving from them when writing their own Series. That's not to say we don't love to see people opening up the hierarchy and writing their own DataPointSeries subclasses - just that we want to be sure people realize that doing so isn't supported quite yet. (But if you do this anyway, please share your feedback with us - we'd like to ensure that when we do open things up they'll work as smoothly as possible!)

Other Changes

Various UI improvements and bug fixes for issues reported by customers or found internally.

There's lots of good stuff here - we hope you'll enjoy using it as much as we enjoyed developing it!

 

Please click here to download the complete Silverlight 2 and Silverlight 3 source code for a project demonstrating all of the sample charts shown below (and by my two previous introductory blog posts on Charting).

 

Let's begin by looking at an example of the new support for using a CategoryAxis somewhere that it previously wouldn't have been allowed. For this example, we'll look at a fictitious pet shop chain's sales numbers for four types of animals across two different stores. We could go the traditional route and use ColumnSeries for this, but in this case we're looking to visually draw attention to the fact that one store is outselling the other in every category. With the latest Charting bits, it's easy to do this with LineSeries:

LineSeries on a CategoryAxis

Here's the XAML for this chart:

<!-- Chart with shared DataContext -->
<chartingToolkit:Chart
    Title="Pet Shop Sales Numbers"
    DataContext="{StaticResource SalesDataCollection}">
    <!-- West Store data -->
    <chartingToolkit:LineSeries
        Title="West Store"
        ItemsSource="{Binding}"
        IndependentValuePath="Animal"
        DependentValuePath="WestStoreQuantity"/>
    <!-- East Store data -->
    <chartingToolkit:LineSeries
        Title="East Store"
        ItemsSource="{Binding}"
        IndependentValuePath="Animal"
        DependentValuePath="EastStoreQuantity"/>
</chartingToolkit:Chart>

Because the "Animal" property of our data objects is of type string, the LineSeries automatically does what we want and uses a CategoryAxis for the horizontal axis! Note that we're also using the new *Path properties to specify the independent/dependent values instead of the *Binding properties. The new syntax is short, sweet, and to the point - and allows us to focus on visualizing our data instead of creating Bindings.

One other thing worth calling attention to here is the way the data source is associated with both Series. In this case (as in many real-world scenarios), a single data source provides data objects for multiple series. While we could certainly recreate the same Binding to that data source in the ItemsSource property of each Series, there's a slightly easier way! What we do instead is specify the data source as the DataContext of the Chart object and then simply point both ItemsSource properties to it via a simple "{Binding}". The savings in this particular example are relatively minor, but for scenarios with more series, the convenience of being able to perform the actual binding to the data source in just one place becomes quite nice.

 

For the next example, let's revisit one of the charts from last time that used BubbleSeries to visualize the performance of a made-up stock ticker by displaying its price and volume by date. But this time around we'll use two instances of the new AreaSeries class to display the same data. Here's how it looks:

AreaSeries

The XAML is very similar to last time:

<chartingToolkit:Chart
    Title="Stock Performance Revisited"
    LegendTitle="Data">
    <!-- Volume -->
    <chartingToolkit:AreaSeries
        Title="Volume (M)"
        ItemsSource="{StaticResource StockDataCollection}"
        IndependentValuePath="Date"
        DependentValuePath="Volume"/>
    <!-- Price -->
    <chartingToolkit:AreaSeries
        Title="Price ($)"
        ItemsSource="{StaticResource StockDataCollection}"
        IndependentValuePath="Date"
        DependentValuePath="Price"/>
    <chartingToolkit:Chart.Axes>
        <!-- Axis for custom range -->
        <chartingToolkit:LinearAxis
            Orientation="Y"
            Minimum="0"
            Maximum="100"
            ShowGridLines="True"/>
        <!-- Axis for custom labels -->
        <chartingToolkit:DateTimeAxis
            Orientation="X">
            <chartingToolkit:DateTimeAxis.AxisLabelStyle>
                <Style TargetType="chartingToolkit:DateTimeAxisLabel">
                    <Setter Property="StringFormat" Value="{}{0:MMM d}"/>
                </Style>
            </chartingToolkit:DateTimeAxis.AxisLabelStyle>
        </chartingToolkit:DateTimeAxis>
    </chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>

Notice how the semi-transparency of the fill used by AreaSeries makes it easy to plot both sets of data on the same chart. We've also gone ahead and added a title for the Legend via the LegendTitle property on Chart and customized the names of each series via the Title property on Series. We're using the simplified *Path syntax again to keep things easy to understand and we've preserve the previous sample's axis customization to provide a nicely readable representation of the dates being displayed (instead of the longer MM/DD/YYYY form that's used by default).

 

For the last example, we'll need to use the Silverlight 3 version of Charting because we're going to take advantage of the new support for easing functions that's available there. The effort needed to customize the easing function used by a Series is practically nothing - all you need to do is add the highlighted portion below to an existing chart and you're done! It really is this easy:

<chartingToolkit:Chart
    x:Name="EasingDemonstration"
    Title="Easing Demonstration">
    <chartingToolkit:ColumnSeries
        Title="Gelatin Sales">
        <!-- Custom easing function -->
        <chartingToolkit:ColumnSeries.TransitionEasingFunction>
            <ElasticEase EasingMode="EaseOut"/>
        </chartingToolkit:ColumnSeries.TransitionEasingFunction>
        <!-- Specify an appropriate color -->
        <chartingToolkit:ColumnSeries.DataPointStyle>
            <Style TargetType="Control">
                <Setter Property="Background" Value="#ff33ff33"/>
            </Style>
        </chartingToolkit:ColumnSeries.DataPointStyle>
    </chartingToolkit:ColumnSeries>
    <!-- Custom Y axis for range -->
    <chartingToolkit:Chart.Axes>
        <chartingToolkit:LinearAxis
            Orientation="Y"
            Minimum="0"
            Maximum="10"
            ShowGridLines="True"/>
    </chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>

Here's what the corresponding chart looks like:

EasingFunction

Okay, I admit: this is just a static picture and the effect of the easing function can't be appreciated here... However, if you download and build the Silverlight 3 version of the sample code linked to above, you'll find that clicking the "Randomize Data" button changes the data values every time it's pressed. And every time it does, the ElasticEase function specified above causes each of the bars to zoom quickly to their new values and then bounce around for a bit - just as if the bars were made out of gelatin! I'll grant that ElasticEase is probably not the most practical easing function for data visualization purposes, but we found that the default QuadraticEase behavior makes a very noticeable - yet subtle - impact on the viewer. It obviously won't turn a bad chart into a good chart, but it adds just a bit of extra polish that can help a good chart really shine! :)

 

Finally, let's have a look at one of the many Blend improvements we've made in this release. One of the things we wanted to do was help newcomers to Charting get up and running as quickly as possible without having to learn a lot of obscure concepts. To that end, when you drop a new Silverlight 3 Chart onto the design surface with the new Blend 3 Preview, you'll automatically get not just XAML for the chart, but also a bit of sample data and a properly configured ColumnSeries that uses it to display a very simple chart!

Blend 3 Charting User Experience

Of course, we don't expect this to be the same data you intended to visualize, but the automatic creation and configuration of the complete end-to-end scenario should help people understand just what it takes to create a chart. Furthermore, it's easy to begin experimenting with the objects and data that are automatically created in order to learn more and develop an understanding about how the different properties work together. To make that experimentation even more rewarding (translation: less frustrating) we've done some work internally to suppress many of the exceptions that result from the invalid configurations that are so easy to accidentally trigger when interactively manipulating a chart in the designer. There's still a lot more that can be done to improve the design-time experience for Charting - but I hope you'll agree that changes like this move us strongly in the right direction!

 

We've had a great time working on the March 09 release of Charting and hope you're as excited by the new functionality as we are! You can always browse the live Charting sample page for Silverlight 2 or Silverlight 3 to find out more - then please download the March 09 Toolkit release - for Silverlight 2 or Silverlight 3 - and start playing around with the new stuff! And as always, if you have questions about Charting, please ask them in the Silverlight Controls forum where there are plenty of folks who are happy to help out (including us!). If you run into problems and think you've found a bug, please report it with the Issue Tracker on the Toolkit's CodePlex site so that we'll know about it and others can tell us if they're seeing the same behavior.

Thank you very much for taking an interest in Charting - I hope you have fun with the new bits!! :)

 

PS - I'll be posting an update to my ChartBuilder application/tutorial (Background reading: Introduction, Update, New Release) in the next day or so. ChartBuilder itself hasn't changed much since last time, but there's going to be something special about it this time that think you'll agree is pretty exciting! :)