Pie Chart Easy AS….

I wanted to add an animated pie chart to my previous post.  The samples from the Toolkit are terrific, but sometimes it is difficult to find the easiest, most cookbook like process; so for those of you who might want to do the same, here is an annotated walk-through of creating this animated pie chart:

I began by opening Visual Studio and creating a new Silverlight Application, and saying no to the offer to create a Web application.

The UI, created in Page.xaml consists of the header and the Chart, placed in a Grid. You can easily do this in Blend or, in this case the layout is so simple, I hard-coded it in Xaml:

<UserControl 
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;
assembly=System.Windows.Controls.DataVisualization.Toolkit"

x:Class="Pi.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="350" Height="350">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>



<TextBlock Text="Division of Effort Among Activities"
Margin="20,10,0,0"
Grid.Row="0"
FontFamily="Georgia"
FontSize="18"
Foreground="Blue" />

<chartingToolkit:Chart x:Name="ActivityChart"
Margin="20"
Grid.Row="1">
<chartingToolkit:Chart.Series>
<chartingToolkit:PieSeries Title="Fall Activity"
IndependentValueBinding="{Binding Name}"
DependentValueBinding="{Binding Value}" />
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>

    </Grid>
</UserControl>

The key Xaml here is the Chart control which contains a Series which in turn contains a PieSeries. The PieSeries has an Independent and a Dependent set of values, as explained in some detail here.

To have a set of values to bind to, I created a data class that I named Activity. The Activity class has three properties of interest:

  • Name – used to hold the independent value
  • Value – used to hold the dependent value
  • Activities – returns a list of Activity objects
   1: using System;

   2: using System.Collections.Generic;

   3:  

   4: namespace Pi

   5: {

   6:   public class Activity

   7:   {

   8:     public string Name { get; set; }

   9:     public double Value { get; set; }

  10:     

  11:     // static property to retrieve 

  12:     // List of Activity objects

  13:     public static List<Activity> Activities

  14:     {

  15:       get

  16:       {

  17:         // value of 5 and names of activities are hard coded

  18:         // Generalizing is left as an exercise for the ambitious

  19:         var percentages = FillPercentages(5);  

  20:         var activitiesList = new List<Activity>()

  21:              {

  22:                new Activity() {Name = "RiaServices", Value = percentages[0]},

  23:                new Activity() {Name = "DataGrid", Value = percentages[1]},

  24:                new Activity() {Name = "Behaviors", Value = percentages[2]},

  25:                new Activity() {Name = "VSM", Value = percentages[3]},

  26:                new Activity() {Name = "SampleData", Value = percentages[4]}

  27:              };

  28:         return activitiesList;

  29:       }

  30:     }

  31:  

  32:     // fill List<Double> with n doubles that sum to 1.0

  33:     // where n = numDoubles

  34:     private static List<Double> FillPercentages(int numDoubles)

  35:     {

  36:       var pctgs = new List< Double >();

  37:       var r = new Random();

  38:       double total = 0.0;

  39:       

  40:       for (int i = 0; i < numDoubles-1;)

  41:       {

  42:  

  43:         double val = r.NextDouble();

  44:         if ( val + total < 1.0)

  45:         {

  46:           pctgs.Add(val);

  47:           ++i;

  48:         }

  49:       }

  50:       pctgs.Add( 1.0 - total );  // final value

  51:       return pctgs;

  52:     } 

  53:  

  54:   }   // end class

  55: }     // end namespace

On line 13 we start the definition of the Activities property (only a get accessor is implemented)

On line 19 we delegate to a helper method generating 5 random values between 0 and 1 that together sum to 1. These will be treated as the relative percentages reflected in the pie chart.

Lines 20-27 fill our five hard-coded activities with the generated percentages and on line 28 we return the List of Activity objects we just created.

The code-behind for MainPage uses a dispatch timer to call a helper method FillPie every 4 seconds.

The helper method sets the ItemSource property on the PieSeries to whatever is returned by the static Activities property of the Activity class. Retrieving that property causes the percentages to be regenerated, and the chart is redrawn.

 

   1: using System;

   2: using System.Windows.Controls;

   3: using System.Windows.Controls.DataVisualization.Charting;

   4: using System.Windows.Threading;

   5:  

   6: namespace Pi

   7: {

   8:   public partial class MainPage : UserControl

   9:   {

  10:     public MainPage()

  11:     {

  12:       InitializeComponent();

  13:       Animate();

  14:     }

  15:  

  16:     private void Animate()

  17:     {

  18:       var timer = new DispatcherTimer();

  19:       timer.Start();  // Run once for display

  20:       // lambda syntax - same as

  21:       // timer.Tick +=new EventHandler(FillPie);

  22:       // but then you'd need FillPie to take an object and event args

  23:       timer.Tick +=

  24:           ( ( s, args ) => FillPie() );  // every tick call FillPie

  25:  

  26:       // http://msdn.microsoft.com/en-us/library/cc316852.aspx

  27:       timer.Interval = new TimeSpan( 0, 0, 4 ); // 4 seconds

  28:       timer.Start();

  29:     }

  30:  

  31:  

  32:     private void FillPie()

  33:     {

  34:       var cs = ActivityChart.Series[0] as PieSeries;

  35:       if ( cs != null )

  36:       {

  37:         // generating the data is handled by the static property

  38:         cs.ItemsSource = Activity.Activities;

  39:       }

  40:       else

  41:       {

  42:         throw new InvalidCastException( "Expected Series[0] to be a column" );

  43:       }  // end else

  44:     }    // end method

  45:   }      // end class

  46: }        // end namespace

Notice that on lines 23 and 24 we register the FillPie method with the event using lambda notation; this makes short work of using a method that does not happen to need the standard arguments (object, eventArgs).

Timer.Start is called on line 19 to cause an immediate drawing of the pie, and then again on line 28 to implement the new time interval.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in z Silverlight Archives. Bookmark the permalink.