In this short post, we’ll show you how to use summary calculations and chart customizations to fine tune data presentation within your next WPF app (we will display the most popular pies for Thanksgiving dinner).
The inspiration and data for this post came from: Popular Thanksgiving Pies Pie Chart
Summary Calculation
Let’s assume we have a JSON file that includes our Thanskgiving survey results. The file contains items such as:
[{"Name":"Apple"},{"Name":"Key Lime"},{"Name":"Pumpkin"},{"Name":"Apple"},{"Name":"Key Lime"}, … ]
We will use the COUNT summary function to generate our Pie chart from a single-dimension array of repeating elements.
<dxc:ChartControl
x:Name="chart"
ToolTipEnabled="False">
<dxc:ChartControl.Titles>
<dxc:Title
HorizontalAlignment="Center"
Content="Thanksgiving Pie Chart"/>
</dxc:ChartControl.Titles>
<dxc:SimpleDiagram2D>
<dxc:PieSeries2D
DisplayName="Pie" HoleRadiusPercent="0" LabelsVisibility="True"
ArgumentDataMember="Name" ColorDataMember="Name"
DataSource="{Binding Data}">
<dxc:PieSeries2D.Summary>
<dxc:Summary>
<dxc:CountSummaryFunction />
</dxc:Summary>
</dxc:PieSeries2D.Summary>
<dxc:PieSeries2D.Label>
<dxc:SeriesLabel
ConnectorVisible="False" Indent="20" TextPattern="{}{A} {V}%">
</dxc:SeriesLabel>
</dxc:PieSeries2D.Label>
</dxc:PieSeries2D>
</dxc:SimpleDiagram2D>
</dxc:ChartControl>
public partial class MainWindow : Window {
public class Item {
public string Name { get; set; }
}
public List<Item> data;
public List<Item> Data {
get {
return data;
}
}
public MainWindow() {
InitializeComponent();
chart.DataContext = this;
using (StreamReader r = new StreamReader(@"..\..\Data\votes.txt")) {
string json = r.ReadToEnd();
data = JsonConvert.DeserializeObject<List<Item>>(json);
}
}
}
Now that we have our basic pie chart, we’ll use our WPF Chart control’s appearance customization options to refine our basic layout and deliver a more refined user experience.
Appearance Customization
In this step, we’ll use icons to make Series Labels more informative. We will display the image of a pie’s primary ingredient next to its percentage value (by customizing the Series Label template). We will need to add image files for each ingredient to our project. These images will be loaded based on the segment’s argument value in the custom value converter class.
<dxc:SeriesLabel
ConnectorVisible="False"
Indent="20"
TextPattern="{}{V}%">
<dxc:SeriesLabel.ElementTemplate>
<DataTemplate>
<Border
Grid.Column="1"
Background="{Binding Color, Converter={StaticResource brushOverlayConverter}}"
CornerRadius="8,8,8,8"
Padding="8,5,8,5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image
Grid.RowSpan="2" Width="32" Height="32" Margin="0,0,5,0"
Source="{Binding SeriesPoint.Argument, Converter={StaticResource ResourceKey=itemToBitmapConverter}}" />
<TextBlock
Grid.Column="1" FontSize="14" FontWeight="Bold"
Text="{Binding SeriesPoint.Argument}" />
<TextBlock
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" FontSize="12"
Text="{Binding Text}" />
</Grid>
</Border>
</DataTemplate>
</dxc:SeriesLabel.ElementTemplate>
</dxc:SeriesLabel>
public class ItemToBitmapConverter : MarkupExtension, IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
string path = @"..\..\Images\" + value.ToString() + ".svg";
var svgStream = File.OpenRead(path);
if (svgStream == null)
return null;
var svgImage = SvgLoader.LoadFromStream(svgStream);
var size = new Size(svgImage.Width * ScreenHelper.ScaleX, svgImage.Height * ScreenHelper.ScaleX);
return WpfSvgRenderer.CreateImageSource(svgImage, size, null, null, true);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider) {
return this;
}
}
In our final step, we will define a fixed color schema using our WPF Chart’s Colorizer feature. We will also create a custom Pie Series template so our chart resembles an actual pie.
<dxc:PieSeries2D.Colorizer>
<dxc:KeyColorColorizer>
<dxc:KeyColorColorizer.Keys>
<System:String>Apple</System:String>
<System:String>Key Lime</System:String>
<System:String>Pumpkin</System:String>
<System:String>Blueberry</System:String>
<System:String>Chocolate</System:String>
<System:String>Pecan</System:String>
<System:String>Cherry</System:String>
<System:String>Lemon Meringue</System:String>
<System:String>Strawberry</System:String>
</dxc:KeyColorColorizer.Keys>
<dxc:KeyColorColorizer.Palette>
<dxc:CustomPalette>
<dxc:CustomPalette.Colors>
<Color>#82a74b</Color>
<Color>#bfc24f</Color>
<Color>#ea832f</Color>
<Color>#4a76c6</Color>
<Color>#6d4c41</Color>
<Color>#795548</Color>
<Color>#a33f42</Color>
<Color>#f8c028</Color>
<Color>#cf4d52</Color>
</dxc:CustomPalette.Colors>
</dxc:CustomPalette>
</dxc:KeyColorColorizer.Palette>
</dxc:KeyColorColorizer>
</dxc:PieSeries2D.Colorizer>
<dxc:PieSeries2D.Model>
<dxc:CustomPie2DModel>
<dxc:CustomPie2DModel.PointTemplate>
<ControlTemplate>
<Grid>
<Image>
<Image.Source>
<dx:SvgImageSource Uri="Images/Pie.svg" />
</Image.Source>
</Image>
<Ellipse Margin="20" Fill="{Binding PointColor, Converter={StaticResource brushOverlayConverter}}" />
</Grid>
</ControlTemplate>
</dxc:CustomPie2DModel.PointTemplate>
</dxc:CustomPie2DModel>
</dxc:PieSeries2D.Model>
Download Example
The code for this simple project is available in our GitHub repository (How to: Implement a Custom Model for a Pie Chart Slice). Should you have any questions about implementation, please post your questions below.