The Weekly Source Code 36 - PDC, BabySmash and Silverlight Charting
First, let me remind you that in my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you thirty-fifth in a infinite number of posts of "The Weekly Source Code."
At the end of my crazy babies talk at PDC (Tips on how I prepared here) I had a big demo where I gave a URL to a Silverlight version of BabySmash that Grant and I built for the show. You can watch the presentation online if you like and fast forward to the end (around 60 minutes in) and see the big demo. Basically we had the Silverlight BabySmash talk via ADO.NET Data Services (I'll post in detail in the near future) to a SQL backend. Then I had an MVC reporting site that had some charts that would update as folks smashed. There were over 90,000 smashes during the talk.
The chart was updating as folks were smashing and we even had a Baby vs. Baby fight break out where the "A" people and the "J" people were going at it. Jeff Atwood started the bloodbath with this tweet as he urged on the overflow room along with Phil Haack. That man's trouble, I tell you.
In the talk, I started out with a old .NET 1.1 chart from 2003 and showed it working, unchanged, in ASP.NET 3.5 SP1. It's just a nice reminder that things usually work just as they should. Then I upgraded it to a new .NET 4.0 ASP.NET Chart that I'll blog about in detail soon. Then, I showed the final site with the new Silverlight Charts. Tim Heuer has a great post on how to databind with these new charts.
What's really cool about these Silverlight Charts is that they are Ms-PL (Microsoft Public License) which is a REALLY relaxed license. They're released as part of the larger Silverlight Toolkit up at http://www.codeplex.com/Silverlight. There's a bunch of controls in there. It is a preview release though, so things will change, and hopefully only get better:
- Components in the Preview Quality Band
- Components in the Stable Quality Band
You can check out the Toolkit Chart samples and run them yourself here. It's nice that the chart sampler actually includes the source code within the Silverlight sample app. You can browse dozens of charts, then switch tabs and see the XAML and code-behind. This all lives in Microsoft.Windows.Controls.DataVisualization, the namespace (so far) for these controls.
My reporting page included a Silverlight Chart and a Virtual Earth control to show where people were smashing from. The data is coming from the Astoria ADO.NET Data Service, which is easy to get to via either JavaScript or from Silverlight.
You add the charts to your Silverlight application by adding a reference to the assembly then assigning a namespace to them:
xmlns:charting="clr-namespace:Microsoft.Windows.Controls.DataVisualization.Charting;assembly=Microsoft.Windows.Controls.DataVisualization"
xmlns:datavis="clr-namespace:Microsoft.Windows.Controls.DataVisualization;assembly=Microsoft.Windows.Controls.DataVisualization"
Them, lay them out. I've got two charts here, one column and one pie. I also did some stuff like the linear gradient for the background, etc. Still, pretty simple.
<charting:Chart Grid.Column="0" Height="300" StylePalette="{StaticResource PaletteColors}" Style="{StaticResource ChartStyle1}" >
<charting:Chart.Background>
<LinearGradientBrush EndPoint="1.332,1.361" StartPoint="-0.107,-0.129">
<GradientStop Color="#FF6CA9D5"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</charting:Chart.Background>
<charting:Chart.Axes>
<charting:Axis x:Name="colAxis" Orientation="Vertical" AxisType="Linear" Minimum="0" Maximum="1"></charting:Axis>
</charting:Chart.Axes>
<charting:Chart.Series>
<charting:ColumnSeries x:Name="colSeries" ItemsSource="{StaticResource BasicValues}" DependentValueBinding="{Binding Count}" IndependentValueBinding="{Binding Character}" Title="Character">
</charting:ColumnSeries>
</charting:Chart.Series>
</charting:Chart>
<charting:Chart Style="{StaticResource ChartStyle1}" Grid.Column="1" Height="300" StylePalette="{StaticResource PaletteColors}" >
<charting:Chart.Axes>
<charting:Axis Orientation="Vertical" AxisType="Linear" Maximum="100000"></charting:Axis>
</charting:Chart.Axes>
<charting:Chart.Series>
<charting:PieSeries x:Name="pieSeries" ItemsSource="{StaticResource BasicValues}" DependentValueBinding="{Binding Count}" IndependentValueBinding="{Binding Character}" Title="Character">
</charting:PieSeries>
</charting:Chart.Series>
</charting:Chart>
We had a generic list of "CharacterSmash" data, as in List<CharacterSmash> that we'd be binding to the chart.
private readonly List<CharacterSmash> characterData = new List<CharacterSmash>();
For the purposes of the presentation, I just polled for the data by making an asynchronous call to the service, then updating the bar and pie chart when it returned:
private void RequestSmashCountData()
{
var container = new SmashMetricsContainer(new Uri("/BabySmashPDC/SmashService.svc", UriKind.Relative));
// Setup data query
var query = container.SmashCount;
// Start the async query
query.BeginExecute((asyncResult =>
{
// Get the matching results from the service call
var matches = query.EndExecute(asyncResult);
UpdateCharacterData(matches);
UpdateBarChart();
UpdatePieChart();
}), null);
}
See how the BeginExecute includes the "do this when you return" as a lambda? It's a tidy syntax.
UPDATE: Tim Heuer emailed me to say that we're re-databinding the results. Instead, he wisely points out:
"On the code where you are getting the smash metrics for the silverlight charts…I see that you are re-binding the data?
If you bind to an observablecollection and just change that the chart should change with the data…including the Y-axis growth."
Excellent point! Tim's right. The way I'm doing it works, but it's old school. If I just updated a ObservableCollection the chart would notice the changes and update itself.
The updates are also clean, just databinding the results:
private void UpdateBarChart()
{
var axis = (Axis)FindName("colAxis");
if (axis != null)
axis.Maximum = GetMaximumCount() + 50;
var colSeriesControl = (ColumnSeries)FindName("colSeries");
if (colSeriesControl != null)
colSeriesControl.ItemsSource = characterData;
}
All we had to do that was interesting at all was to make sure the Y-axis grew as the data grew.
Who do we have to thank for this charting control? David Anson is who. Basically he was the Primary Dev and only Tester on the whole thing, and you should check out his blog for lots of inside information on charting in Silverlight.
UPDATE: David had development help from Jafar Husain, Jeremy Sheldon, Delian Tchoparinov, Alex Gorev and Sean Boon and designer Mehdi Slaoui Andaloussi.
If making a complex chart seems daunting, David has ChartBuilder that you run now in your browser. It'll generate and show you the XAML you need for your chart.
There was so much announced at PDC, I wanted to make sure that folks heard about this important release that might have been lost in the shuffle. Even better, the source is open so if you don't like it, change it.
Related Links
- Make your Silverlight Applications Smaller with ReXapper
- ChartBuilder and Intro to Charting with Silverlight
- Silverlight Tookit Samples
- http://www.codeplex.com/Silverlight
- Shawn Burke's blog (he's the boss)
- Silverlight Control Support Forum
- Tim Heuer on the Silverlight Toolkit
- Jesse Liberty's Silverlight Blog
- Grant Archibald on Silverlight and BabySmash
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Your praise for Silverlight Charting is *very* much appreciated! But it was by no means a solo effort and I apologize if I said otherwise! While I was indeed the only tester and also a primary developer, I was NOT the only dev. I specifically call out Jafar Husain as a key developer in my introductory post to Charting and readers may be interested to know that he has already blogged about some of the gory implementation details of Charting. The other developers who checked in Charting code were Jeremy Sheldon and Delian Tchoparinov from the SQL Data Visualization team. The Charting project also benefitted greatly from the advice and expertise of Alex Gorev and Sean Boon (also of SQL DV) and the mad last-minute design skillz of Mehdi Slaoui Andaloussi. And we accrued tremendous benefits from our close association with the many fine individuals on the Controls side of the Silverlight Toolkit team who created much of the infrastructure that Charting takes for granted (like the build system, samples browser, CodePlex site, etc.). To fully convey the extent of my appreciation for everyone's contribution, I'm tempted to share the "thank you" email I sent the team last week - but it's full of private jokes that would make absolutely no sense to outsiders... :)
This is a great post. Charts are so useful in many business apps.
Thanks for the info,
Catto
This is great demo.
However I have one serious complaint about silverlight having used it since version 1.0 and it doesn't have a Print API!! You got to be kidding about "Report" that can't be printed.
It is very important to have Print APIs if Business Apps have to successful in API. Most small business software enter the data in the system so that they could use it later for Reporting. And without Printing, Reporting is indeed Crippled and so are us guys who wish to Program in silverlight and create Business Apps that runs in the browser :)
The demo however is great.
Comments are closed.
In the Silverlight Charting area, I think we can save you some more code. Off my head I see that perhaps you're binding the data each time you get the metrics and looking at the Y-axis. One of the great things about the charting is that you can bind to an ObservableCollection. As the data changes in that collection, the chart will react and grow as needed. It's a very nifty little trick.
I'll take a look and send you some updated code. I did a quick sample of simple data here: http://timheuer.com/blog/archive/2008/10/28/silverlight-toolkit-released-with-charting-databinding.aspx but it would be cool to mess with "real" data so I'll take a look.