How To Build XAML Doughnut Chart?
WPF とSilverlightの Infragistics パッケージには、データを視覚化するためのさまざまなグラフが多数含まれています。私たちが見ていくコントロールはドーナツ チャート。
このチャートは、空白の中心を囲む 1 つ以上のリングをサポートします。これは、スライス、ラベル、色を制御するか、内側の半径を構成することによってカスタマイズできます。このブログでは、このコントロールをアプリケーションに追加する方法と、複数のリングを使用してカスタム階層グラフを作成する方法について説明します。

Adding the Doughnut Chart
一般に、WPF プロジェクトを作成するときに最初に行う必要があるのは、参照を追加することです。それを行う簡単な方法があります:
- ツールボックスに移動します。そこには、NetAdvantage WPF コントロールと共有 XAML コントロールが表示されます。
- Drag and drop the “XamDoughnutChart”.
これらの手順を実行すると、Visual Studio によって必要な Infragistics 参照が自動的に追加されることがわかります。

「XamDoughnutChart」をドラッグすると、チャートをホストするために必要なタグが実際に作成されました。次の手順では、次のコードを含めます。
<ig:XamDoughnutChart x:Name="slice">
<ig:XamDoughnutChart.Series>
<ig:RingSeries
LabelMemberPath="Label"
ValueMemberPath="productionShare"
LabelsPosition="BestFit"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource source}}">
</ig:RingSeries>
</ig:XamDoughnutChart.Series>
</ig:XamDoughnutChart>
アイテムのデータ・ソースは、次のようにページの cs ファイルに生成されます。
public class Category
{
public string Label { get; set; }
public double productionShare { get; set; }
}
public class HierarchalDataCollection : List<Category>
{
public HierarchalDataCollection()
{
this.Add(new Category { Label = "Footwear", productionShare = 46 });
this.Add(new Category { Label = "Clothing", productionShare = 38 });
this.Add(new Category { Label = "Accessories", productionShare = 16 });
}
}

基本的には、ドーナツ チャートを作成する手順です。もちろん、凡例、ブラシ、スライス選択、スライス爆発などの多くのオプションがあり、チャートをさらに魅力的にします。オプションの詳細については、ドキュメントを参照してください。特定のオプションを追加する方法のヒントを提供するだけです。たとえば、スライスを選択して分解し、スタイルを変更できるようにする場合は、AllowSliceExplosionオプションとAllowSliceSelectionオプションを追加し、それらの値を true に設定します。次に、次のようなスライスクリックイベントを作成します。
XAML:
AllowSliceExplosion="True" AllowSliceSelection="True" SliceClick="slice_SliceClick"
C#:
private void slice_SliceClick(object sender, Infragistics.Controls.Charts.SliceClickEventArgs e)
{
e.IsSelected = !e.IsSelected;
e.IsExploded = !e.IsExploded;
}

カスタム階層グラフ
階層データを視覚化する必要がある場合は、グラフの複数リング視覚化を使用できます。3つのリングドーナツチャートを作成し、サブカテゴリに分類されたメインカテゴリを表示します。 そのために、3つのシリーズを作成し、データソースを次のように定義します。
XAML :
<ig:XamDoughnutChart.Series>
<ig:RingSeries
StartAngle="30"
LabelMemberPath="Label"
ValueMemberPath="productionShare"
LabelsPosition="BestFit"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource source}}"
Loaded="RingSeries_Loaded">
</ig:RingSeries>
<ig:RingSeries
StartAngle="30"
LabelMemberPath="Label"
ValueMemberPath="productionShare"
LabelsPosition="BestFit"
Loaded="RingSeries_Loaded"
OthersCategoryThreshold="0" >
</ig:RingSeries>
<ig:RingSeries
StartAngle="30"
LabelMemberPath="Label"
ValueMemberPath="productionShare"
LabelsPosition="BestFit"
OthersCategoryThreshold="0">
</ig:RingSeries>
</ig:XamDoughnutChart.Series>
C# :
public class HierarchalDataCollection : List<Category>
{
public HierarchalDataCollection()
{
this.Add(new Category { Label = "Footwear"});
this.Add(new Category { Label = "Clothing"});
this.Add(new Category { Label = "Accessories"});
this.Add(new Category { Label = "Tech" });
this[0].Children.Add(new Category { Label = "Boots" });
this[0].Children.Add(new Category { Label = "Shoes" });
this[0].Children.Add(new Category { Label = "Sneakers" });
this[0].Children.Add(new Category { Label = "Slippers" });
this[1].Children.Add(new Category { Label = "Dresses" });
this[1].Children.Add(new Category { Label = "T-shirts" });
this[1].Children.Add(new Category { Label = "Shirts" });
this[1].Children.Add(new Category { Label = "Pants" });
this[2].Children.Add(new Category { Label = "Bag" });
this[2].Children.Add(new Category { Label = "Jewelry" });
this[2].Children.Add(new Category { Label = "Scarf" });
this[3].Children.Add(new Category { Label = "PC"});
this[3].Children.Add(new Category { Label = "Laptops"});
this[3].Children.Add(new Category { Label = "Tablets"});
this[3].Children.Add(new Category { Label = "Phones"});
this[0].Children[0].Children.Add(new Category { Label = "B1", productionShare = 3 });
this[0].Children[0].Children.Add(new Category { Label = "B3", productionShare = 3 });
this[0].Children[0].Children.Add(new Category { Label = "B4", productionShare = 4 });
this[0].Children[1].Children.Add(new Category { Label = "S1", productionShare = 3 });
this[0].Children[1].Children.Add(new Category { Label = "S2", productionShare = 5 });
this[0].Children[1].Children.Add(new Category { Label = "S3", productionShare = 4 });
this[0].Children[2].Children.Add(new Category { Label = "Sn1", productionShare = 6 });
this[0].Children[2].Children.Add(new Category { Label = "Sn2", productionShare = 9 });
this[0].Children[3].Children.Add(new Category { Label = "SL1", productionShare = 2 });
this[0].Children[3].Children.Add(new Category { Label = "Sl2", productionShare = 4 });
this[0].Children[3].Children.Add(new Category { Label = "Sl3", productionShare = 3 });
this[1].Children[0].Children.Add(new Category { Label = "d1", productionShare = 3 });
this[1].Children[0].Children.Add(new Category { Label = "d2", productionShare = 3 });
this[1].Children[0].Children.Add(new Category { Label = "d3", productionShare = 2 });
this[1].Children[1].Children.Add(new Category { Label = "t1", productionShare = 5 });
this[1].Children[1].Children.Add(new Category { Label = "t2", productionShare = 4 });
this[1].Children[1].Children.Add(new Category { Label = "t3", productionShare = 2 });
this[1].Children[1].Children.Add(new Category { Label = "t4", productionShare = 1 });
this[1].Children[2].Children.Add(new Category { Label = "sh1", productionShare = 3 });
this[1].Children[2].Children.Add(new Category { Label = "sh2", productionShare = 3 });
this[1].Children[2].Children.Add(new Category { Label = "sh3", productionShare = 2 });
this[1].Children[3].Children.Add(new Category { Label = "p1", productionShare = 4 });
this[1].Children[3].Children.Add(new Category { Label = "p2", productionShare = 6 });
this[2].Children[0].Children.Add(new Category { Label = "bag1", productionShare = 2 });
this[2].Children[0].Children.Add(new Category { Label = "bag2", productionShare = 1 });
this[2].Children[0].Children.Add(new Category { Label = "bag3", productionShare = 4 });
this[2].Children[1].Children.Add(new Category { Label = "j1", productionShare = 3 });
this[2].Children[1].Children.Add(new Category { Label = "j2", productionShare = 2 });
this[2].Children[2].Children.Add(new Category { Label = "sc1", productionShare = 1 });
this[2].Children[2].Children.Add(new Category { Label = "sc2", productionShare = 1 });
this[2].Children[2].Children.Add(new Category { Label = "sc3", productionShare = 1 });
this[2].Children[2].Children.Add(new Category { Label = "sc4", productionShare = 1 });
this[3].Children[0].Children.Add(new Category { Label = "pc1", productionShare = 3 });
this[3].Children[0].Children.Add(new Category { Label = "pc2", productionShare = 2 });
this[3].Children[0].Children.Add(new Category { Label = "pc3", productionShare = 5 });
this[3].Children[1].Children.Add(new Category { Label = "l1", productionShare = 4 });
this[3].Children[1].Children.Add(new Category { Label = "l2", productionShare = 3 });
this[3].Children[2].Children.Add(new Category { Label = "tab1", productionShare = 4 });
this[3].Children[2].Children.Add(new Category { Label = "tab2", productionShare = 3 });
this[3].Children[2].Children.Add(new Category { Label = "tab3", productionShare = 3 });
this[3].Children[2].Children.Add(new Category { Label = "tab4", productionShare = 3 });
this[3].Children[3].Children.Add(new Category { Label = "ph1", productionShare = 2 });
this[3].Children[3].Children.Add(new Category { Label = "ph2", productionShare = 3 });
this[3].Children[3].Children.Add(new Category { Label = "ph3", productionShare = 2 });
this[3].Children[3].Children.Add(new Category { Label = "ph4", productionShare = 1 });
}
}
これで、視覚化するすべてのデータが揃いました。階層的に見えるようにしているので、すべてのカテゴリの異なるスライスの色が似ていると便利です。私たちがやろうとしていることは、最も内側のリングから基本的なブラシを取り、一歩ごとにそれを明るくすることです。次のコードでは、最初にリングに何人の子がいるかを把握し、次に子に完全に一致するブラシのコレクションを作成することがわかります。
private void RingSeries_Loaded(object sender, RoutedEventArgs e)
{
var ringSeries = (sender as RingSeries);
var count = ringSeries.Ring.ArcItems[0].SliceItems.Count();
var brushes = ringSeries.Brushes;
BrushCollection brushesMatch = new BrushCollection();
for (var i = 0; i < count; i++)
{
var childrenCount = (ringSeries.ItemsSource as List<Category>)[i].Children.Count();
var child = (ringSeries.ItemsSource as List<Category>)[i].Children;
var brush = brushes[i];
for (var j = 0; j < childrenCount; j++)
{
double step = 1 / (double)childrenCount;
Random rand = new Random();
double val = (1 + j) * step - .3;
brushesMatch.Add(brush.GetLightened(val));
}
}
ringSeries.Chart.Series[ringSeries.Ring.Index + 1].Brushes = brushesMatch;
}

表示するデータに応じて、ランダムな色合いでブラッシングするなど、子の色付け方法を変更できます。Solid、Ril、Linear Gradientなど、使用するブラシの種類に関係なく、GetLightend拡張メソッドがそれを処理します。
private void RingSeries_Loaded(object sender, RoutedEventArgs e)
{
var ringSeries = (sender as RingSeries);
var count = ringSeries.Ring.ArcItems[0].SliceItems.Count();
var brushes = ringSeries.Brushes;
BrushCollection brushesMatch = new BrushCollection();
for (var i = 0; i < count; i++)
{
var childrenCount = (ringSeries.ItemsSource as List<Category>)[i].Children.Count();
var child = (ringSeries.ItemsSource as List<Category>)[i].Children;
var brush = brushes[i];
for (var j = 0; j < childrenCount; j++)
{
Random rand = new Random();
if (j % 2 == 0)
{
double val = Math.Round((rand.NextDouble() / 4), 2);
brushesMatch.Add(brush.GetLightened(-val));
}
else
{
double val = Math.Round((rand.NextDouble() / 3), 2) + 0.2;
brushesMatch.Add(brush.GetLightened(val));
}
}
}
ringSeries.Chart.Series[ringSeries.Ring.Index + 1].Brushes = brushesMatch;
}

ドーナツチャートについて詳しく知りたい場合は、製品のページをご覧ください。
A WPF sample and Silverlight sample.
Twitterでフォローできます@Infragistics連絡を取り合うフェイスブック,Google+のそしてLinkedInの!