How to build XAML Doughnut Chart

Marina Stoyanova / Thursday, October 24, 2013

The Infragistics packages for WPF and Silverlight contain a lot of diverse charts for data visualization. The control that we are going to look at is The Doughnut Chart. This chart supports one or more rings surrounding a blank center. It can be customized by controlling the slices labels and colors or by configuring the inner radius. In this blog you can learn how to add this control to your application and how to create a custom hierarchical chart by using multiple rings.

XAML Doughnut Chart with custom brushes

Adding the Doughnut Chart

Generally, when you create your WPF project the first thing you need to do is to add the references. There is an easy way to do that:

  1. Go to the toolbox. There you will see the NetAdvantage WPF and Shared XAML controls.
  2. Drag and drop the “XamDoughnutChart”.

When you do those steps you will see that Visual Studio automatically adds the required Infragistics references.

 

When you dragged “XamDoughnutChart”, you actually created the needed tag to host our chart. The next step is to include the following code:

  1. <ig:XamDoughnutChart x:Name="slice">
  2.     <ig:XamDoughnutChart.Series>
  3.         <ig:RingSeries
  4.             LabelMemberPath="Label"
  5.             ValueMemberPath="productionShare"
  6.             LabelsPosition="BestFit"
  7.             ItemsSource="{Binding Mode=OneWay, Source={StaticResource source}}">
  8.         ig:RingSeries>
  9.     ig:XamDoughnutChart.Series>
  10. ig:XamDoughnutChart>

The data source for the items is generated in the cs file of the page as follows:

  1. public class Category
  2. {
  3.      public string Label { get; set; }
  4.      public double productionShare { get; set; }
  5. }
  6.  
  7. public class HierarchalDataCollection : List<Category>
  8. {
  9.      public HierarchalDataCollection()
  10.      {
  11.          this.Add(new Category { Label = "Footwear", productionShare = 46 });
  12.          this.Add(new Category { Label = "Clothing", productionShare = 38 });
  13.          this.Add(new Category { Label = "Accessories", productionShare = 16 });
  14.      }
  15. }

 

Basic XAML doughnut chart

 

Basically those are the steps to create a Doughnut Chart. Of course it has many options like legend, brush, slice Select and Slice Explode, which will make your chart even more attractive. More about the options you can find in the documentation. I will just give you a hint how to add a particular option. For example if you want to be able to select a slice and thus make it explode and change its style add the AllowSliceExplosion and AllowSliceSelection options and set their values to true. Then create a slice click event like that:

XAML:

  1. AllowSliceExplosion="True"
  2. AllowSliceSelection="True"
  3. SliceClick="slice_SliceClick"

C#:

  1. private void slice_SliceClick(object sender, Infragistics.Controls.Charts.SliceClickEventArgs e)
  2. {
  3.     e.IsSelected = !e.IsSelected;
  4.     e.IsExploded = !e.IsExploded;
  5. }

 

Exploded XAML doughnut chart

 

Custom hierarchical  chart

When you need to visualize hierarchical data, you can use the multiple ring visualization of the chart. We are going to create three rings doughnut chart, which will display the main categories broken down into subcategories.  For that purpose we are going to create three series and define our data source like this:

XAML :

  1. <ig:XamDoughnutChart.Series>
  2.     <ig:RingSeries
  3.                                         StartAngle="30"
  4.         LabelMemberPath="Label"
  5.         ValueMemberPath="productionShare"
  6.         LabelsPosition="BestFit"
  7.         ItemsSource="{Binding Mode=OneWay, Source={StaticResource source}}"
  8.                             Loaded="RingSeries_Loaded">
  9.     ig:RingSeries>
  10.     <ig:RingSeries
  11.                                         StartAngle="30"
  12.         LabelMemberPath="Label"
  13.         ValueMemberPath="productionShare"
  14.         LabelsPosition="BestFit"
  15.         Loaded="RingSeries_Loaded"
  16.                             OthersCategoryThreshold="0" >
  17.     ig:RingSeries>
  18.     <ig:RingSeries
  19.                                         StartAngle="30"
  20.         LabelMemberPath="Label"
  21.         ValueMemberPath="productionShare"
  22.         LabelsPosition="BestFit"
  23.         OthersCategoryThreshold="0">
  24.     ig:RingSeries>
  25. ig:XamDoughnutChart.Series>

C# :

  1. public class HierarchalDataCollection : List<Category>
  2.     {
  3.         public HierarchalDataCollection()
  4.         {
  5.             
  6.             this.Add(new Category { Label = "Footwear"});
  7.             this.Add(new Category { Label = "Clothing"});
  8.             this.Add(new Category { Label = "Accessories"});
  9.             this.Add(new Category { Label = "Tech" });
  10.  
  11.             
  12.             this[0].Children.Add(new Category { Label = "Boots" });
  13.             this[0].Children.Add(new Category { Label = "Shoes" });
  14.             this[0].Children.Add(new Category { Label = "Sneakers" });
  15.             this[0].Children.Add(new Category { Label = "Slippers" });
  16.  
  17.             this[1].Children.Add(new Category { Label = "Dresses" });
  18.             this[1].Children.Add(new Category { Label = "T-shirts" });
  19.             this[1].Children.Add(new Category { Label = "Shirts" });
  20.             this[1].Children.Add(new Category { Label = "Pants" });
  21.  
  22.             this[2].Children.Add(new Category { Label = "Bag" });
  23.             this[2].Children.Add(new Category { Label = "Jewelry" });
  24.             this[2].Children.Add(new Category { Label = "Scarf" });
  25.  
  26.             this[3].Children.Add(new Category { Label = "PC"});
  27.             this[3].Children.Add(new Category { Label = "Laptops"});
  28.             this[3].Children.Add(new Category { Label = "Tablets"});
  29.             this[3].Children.Add(new Category { Label = "Phones"});
  30.  
  31.             
  32.             this[0].Children[0].Children.Add(new Category { Label = "B1", productionShare = 3 });
  33.             this[0].Children[0].Children.Add(new Category { Label = "B3", productionShare = 3 });
  34.             this[0].Children[0].Children.Add(new Category { Label = "B4", productionShare = 4 });
  35.  
  36.             this[0].Children[1].Children.Add(new Category { Label = "S1", productionShare = 3 });
  37.             this[0].Children[1].Children.Add(new Category { Label = "S2", productionShare = 5 });
  38.             this[0].Children[1].Children.Add(new Category { Label = "S3", productionShare = 4 });
  39.  
  40.             this[0].Children[2].Children.Add(new Category { Label = "Sn1", productionShare = 6 });
  41.             this[0].Children[2].Children.Add(new Category { Label = "Sn2", productionShare = 9 });
  42.  
  43.             this[0].Children[3].Children.Add(new Category { Label = "SL1", productionShare = 2 });
  44.             this[0].Children[3].Children.Add(new Category { Label = "Sl2", productionShare = 4 });
  45.             this[0].Children[3].Children.Add(new Category { Label = "Sl3", productionShare = 3 });
  46.  
  47.            
  48.             this[1].Children[0].Children.Add(new Category { Label = "d1", productionShare = 3 });
  49.             this[1].Children[0].Children.Add(new Category { Label = "d2", productionShare = 3 });
  50.             this[1].Children[0].Children.Add(new Category { Label = "d3", productionShare = 2 });
  51.  
  52.             this[1].Children[1].Children.Add(new Category { Label = "t1", productionShare = 5 });
  53.             this[1].Children[1].Children.Add(new Category { Label = "t2", productionShare = 4 });
  54.             this[1].Children[1].Children.Add(new Category { Label = "t3", productionShare = 2 });
  55.             this[1].Children[1].Children.Add(new Category { Label = "t4", productionShare = 1 });
  56.  
  57.             this[1].Children[2].Children.Add(new Category { Label = "sh1", productionShare = 3 });
  58.             this[1].Children[2].Children.Add(new Category { Label = "sh2", productionShare = 3 });
  59.             this[1].Children[2].Children.Add(new Category { Label = "sh3", productionShare = 2 });
  60.  
  61.             this[1].Children[3].Children.Add(new Category { Label = "p1", productionShare = 4 });
  62.             this[1].Children[3].Children.Add(new Category { Label = "p2", productionShare = 6 });
  63.  
  64.             
  65.             this[2].Children[0].Children.Add(new Category { Label = "bag1", productionShare = 2 });
  66.             this[2].Children[0].Children.Add(new Category { Label = "bag2", productionShare = 1 });
  67.             this[2].Children[0].Children.Add(new Category { Label = "bag3", productionShare = 4 });
  68.  
  69.             this[2].Children[1].Children.Add(new Category { Label = "j1", productionShare = 3 });
  70.             this[2].Children[1].Children.Add(new Category { Label = "j2", productionShare = 2 });
  71.  
  72.             this[2].Children[2].Children.Add(new Category { Label = "sc1", productionShare = 1 });
  73.             this[2].Children[2].Children.Add(new Category { Label = "sc2", productionShare = 1 });
  74.             this[2].Children[2].Children.Add(new Category { Label = "sc3", productionShare = 1 });
  75.             this[2].Children[2].Children.Add(new Category { Label = "sc4", productionShare = 1 });
  76.  
  77.             
  78.             this[3].Children[0].Children.Add(new Category { Label = "pc1", productionShare = 3 });
  79.             this[3].Children[0].Children.Add(new Category { Label = "pc2", productionShare = 2 });
  80.             this[3].Children[0].Children.Add(new Category { Label = "pc3", productionShare = 5 });
  81.  
  82.             this[3].Children[1].Children.Add(new Category { Label = "l1", productionShare = 4 });
  83.             this[3].Children[1].Children.Add(new Category { Label = "l2", productionShare = 3 });
  84.  
  85.             this[3].Children[2].Children.Add(new Category { Label = "tab1", productionShare = 4 });
  86.             this[3].Children[2].Children.Add(new Category { Label = "tab2", productionShare = 3 });
  87.             this[3].Children[2].Children.Add(new Category { Label = "tab3", productionShare = 3 });
  88.             this[3].Children[2].Children.Add(new Category { Label = "tab4", productionShare = 3 });
  89.  
  90.             this[3].Children[3].Children.Add(new Category { Label = "ph1", productionShare = 2 });
  91.             this[3].Children[3].Children.Add(new Category { Label = "ph2", productionShare = 3 });
  92.             this[3].Children[3].Children.Add(new Category { Label = "ph3", productionShare = 2 });
  93.             this[3].Children[3].Children.Add(new Category { Label = "ph4", productionShare = 1 });
  94.         }
  95.     }

 

Now we have all of the data that we want to visualize. As we are making it look hierarchical it would be nice if the different slices for every category have similar color. What we are going to do is take the basic brush from the innermost ring and lighten it with every step. In the following code you can see that first we figure out how many children does the ring have and then we create a collection of brushes that will be a perfect match for the children.

  1. private void RingSeries_Loaded(object sender, RoutedEventArgs e)
  2. {
  3.     var ringSeries = (sender as RingSeries);
  4.     var count = ringSeries.Ring.ArcItems[0].SliceItems.Count();
  5.     var brushes = ringSeries.Brushes;
  6.     BrushCollection brushesMatch = new BrushCollection();
  7.  
  8.     for (var i = 0; i < count; i++)
  9.     {
  10.         var childrenCount = (ringSeries.ItemsSource as List<Category>)[i].Children.Count();
  11.         var child = (ringSeries.ItemsSource as List<Category>)[i].Children;
  12.  
  13.         var brush = brushes[i];
  14.  
  15.         for (var j = 0; j < childrenCount; j++)
  16.         {
  17.             double step = 1 / (double)childrenCount;
  18.             Random rand = new Random();
  19.                 double val = (1 + j) * step - .3;
  20.  
  21.                 brushesMatch.Add(brush.GetLightened(val));
  22.         }
  23.     }
  24.  
  25.     ringSeries.Chart.Series[ringSeries.Ring.Index + 1].Brushes = brushesMatch;
  26. }

 

Custom Hierarchial Doughnut Chart

Depending on the data you are going to display, you can change the way the children are colored, for instance you can brush them in random  tint. No matter  what kind of brush you use- Solid, Radial or Linear Gradient the GetLightend extension method will handle it.

  1. private void RingSeries_Loaded(object sender, RoutedEventArgs e)
  2. {
  3.     var ringSeries = (sender as RingSeries);
  4.     var count = ringSeries.Ring.ArcItems[0].SliceItems.Count();
  5.     var brushes = ringSeries.Brushes;
  6.     BrushCollection brushesMatch = new BrushCollection();
  7.  
  8.     for (var i = 0; i < count; i++)
  9.     {
  10.         var childrenCount = (ringSeries.ItemsSource as List<Category>)[i].Children.Count();
  11.         var child = (ringSeries.ItemsSource as List<Category>)[i].Children;
  12.  
  13.         var brush = brushes[i];
  14.  
  15.         for (var j = 0; j < childrenCount; j++)
  16.         {
  17.             Random rand = new Random();
  18.  
  19.             if (j % 2 == 0)
  20.             {
  21.                 double val = Math.Round((rand.NextDouble() / 4), 2);
  22.                 brushesMatch.Add(brush.GetLightened(-val));
  23.             }
  24.             else
  25.             {
  26.                 double val = Math.Round((rand.NextDouble() / 3), 2) + 0.2;
  27.                 brushesMatch.Add(brush.GetLightened(val));
  28.             }
  29.         }
  30.     }
  31.  
  32.     ringSeries.Chart.Series[ringSeries.Ring.Index + 1].Brushes = brushesMatch;
  33. }

 

Random lighten hierarchical doughnut chart

 

If you want to learn more about the doughnut chart you can check out the product’s page or read my blog:”The Ignite UI Doughnut Chart and its features”.

 

A WPF sample and Silverlight sample.

You can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!