Introduction
I noticed that many people, including myself, have had some difficulties in styling the Chart
control that comes with the Silverlight 4 Toolkit. It is relatively hard to go down the visual tree of the control in order to style the control in the Design view of Expression Blend so as to have a good look and feel. I searched the internet and I got little information on how to go about this process and I decided to work it through myself. I succeeded in restyling the chart control and the majority of the working is done in XAML. I think this article will ease the challenges of other developers/designers facing this same issue.
There are a series of Chart
controls that come with the Silverlight 4 Toolkit, and some of these are listed below:
BarSeries
ColumnSeries
StackedColumnSeries
BubbleSeries
LineSeries
PieSeries
ScatterSeries
I will be discussing two of these series:
ColumnSeries
StackedColumnSeries
Note: You can follow the steps in this article both in Expression Blend 4 and Visual Studio 2010, but I will use Expression Blend 4 in this article. If you do not have the toolkit, it is available at Silverlight Toolkit - CodePlex.
ColumnSeries
With a new Silverlight Application created, I added a Chart
control and edited the Template
. In this article, I decided to delete the Legend and Chart Title from the Template as I will not need them. The Chart Title can still be of use so you can retain yours but the Legend is not useful as we will be using different colors for each column. The chart control now looks like this:
Next, edit the XAML in order to bind the DependentValueBinding
, IndependentValueBinding
properties of the Chart
control to a class which controls each column. The DataPointStyle
property is also bound to a style created to control how the columns look. The XAML is:
<toolkit:Chart x:Name="GasChart" Style="{StaticResource GasChartStyle}"
BorderBrush="{x:Null}" Margin="4,141,0,60">
<toolkit:ColumnSeries
DependentValueBinding="{Binding GasValue}"
IndependentValueBinding="{Binding GasName}"
DataPointStyle="{StaticResource ColorByGradeColumn}"
AnimationSequence="FirstToLast"/>
</toolkit:Chart>
The DataPointStyle
is set to a StaticResource
that sets the ControlTemplate
for the ColumnDataPoint
. At this point, we're changing how the columns will look. This look is a gloss-like column compared to the default ugly look. Paying close attention to the XAML code below, you will notice that the Background
property of the Border
in the ControlTemplate
is bound to one of the properties (GasColor
) of a class. This enables changing the column color dynamically:
<Style x:Key="ColorByGradeColumn" TargetType="toolkit:ColumnDataPoint">
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="{Binding GasColor}"
BorderBrush="{Binding GasColor}"
BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
-->
<StackPanel>
<ContentControl
Content ="{ TemplateBinding IndependentValue }"/>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content ="ppm"/>
</StackPanel>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
-->
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="{Binding GasColor}"
Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now, the class whose properties are bound to the Chart
control is:
public class GasNameValue
{
public double GasValue { get; set; }
public string GasName { get; set; }
public Brush GasColor { get; set; }
}
The method below creates new objects of the class above in a List
where the values for each column are set. The number of objects created will determine the number of columns that your Chart
will contain; most times your data comes from a database or cloud and this process will be dynamic rather than manual. The last line of code sets the ItemsSource
of the Chart
control to the List
object.
private void SetData()
{
List<GasNameValue> gasList = new List<GasNameValue>
{
new GasNameValue
{
GasName = "CO2",
GasValue = 850.0,
GasColor = new SolidColorBrush(Colors.Red)
},
new GasNameValue
{
GasName = "SO2",
GasValue = 700.0,
GasColor = new SolidColorBrush(Colors.Blue)
},
new GasNameValue
{
GasName = "CH4",
GasValue = 820.0,
GasColor = new SolidColorBrush(Colors.Green)
},
new GasNameValue
{
GasName = "NO2",
GasValue = 600.0,
GasColor = new SolidColorBrush(Colors.Yellow)
},
new GasName_Value
{
GasName = "F11",
GasValue = 910.o,
GasColor = new SolidColorBrush(Colors.Purple)
},
new GasNameValue
{
GasName = "F12",
GasValue = 760.0,
GasColor = new SolidColorBrush(Colors.Orange)
},
};
((ColumnSeries)GasChart.Series[0]).ItemsSource = gasList;
}
The final ColumnSeries
looks like this:
StackedColumnSeries
The default StackedColumnSeries
looks like this (and it looks ugly to me):
Now, when working with the StackedColumnSeries
, after adding the Chart
control and editing the Template
as appropriate, you need to add the SeriesDefinition
for each Stacked Column. The number of SeriesDefinition
s that you add determines the number of partitions that you will end up having on each Stacked Column. The XAML below consists of six (6) partitions on each Stacked Column with properties bound to a class:
<toolkit:StackedColumnSeries>
<toolkit:SeriesDefinition
Title="CO2"
ItemsSource="{Binding}"
DependentValueBinding="{Binding GasValue1}"
IndependentValueBinding="{Binding Year}"/>
<toolkit:SeriesDefinition
Title="SO2"
ItemsSource="{Binding}"
DependentValueBinding="{Binding GasValue2}"
IndependentValueBinding="{Binding Year}"/>
<toolkit:SeriesDefinition
Title="NO2"
ItemsSource="{Binding}"
DependentValueBinding="{Binding GasValue3}"
IndependentValueBinding="{Binding Year}"/>
<toolkit:SeriesDefinition
Title="CH4"
ItemsSource="{Binding}"
DependentValueBinding="{Binding GasValue4}"
IndependentValueBinding="{Binding Year}"/>
<toolkit:SeriesDefinition
Title="F11"
ItemsSource="{Binding}"
DependentValueBinding="{Binding GasValue5}"
IndependentValueBinding="{Binding Year}"/>
<toolkit:SeriesDefinition
Title="F12"
ItemsSource="{Binding}"
DependentValueBinding="{Binding GasValue6}"
IndependentValueBinding="{Binding Year}"/>
</toolkit:StackedColumnSeries>
This time around, in order to create a new color for each partition, a ResourceDictionaryCollection
must be added to the chart palette; this ResourceDictionaryCollection
contains six (6) Resource Dictionaries. Each ResourceDictionary
provides the color for each partition on a stack. The XAML is like this:
<toolkit:Chart.Palette>
<toolkit:ResourceDictionaryCollection>
<ResourceDictionary>
<Style x:Key="DataPointStyle" TargetType="toolkit:ColumnDataPoint">
<Setter Property="Background" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="Red" BorderBrush="Red" BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content =" ppm"/>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Red" Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key="DataPointStyle" TargetType="Control">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="Blue"
BorderBrush="Blue" BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content =" ppm"/>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Blue" Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key="DataPointStyle" TargetType="Control">
<Setter Property="Background" Value="Green"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="Green"
BorderBrush="Green" BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content =" ppm"/>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Green" Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary><ResourceDictionary>
<Style x:Key="DataPointStyle" TargetType="Control">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="Yellow"
BorderBrush="Yellow" BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content =" ppm"/>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Yellow" Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key="DataPointStyle" TargetType="Control">
<Setter Property="Background" Value="Purple"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="Purple"
BorderBrush="Purple" BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content =" ppm"/>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Purple" Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key="DataPointStyle" TargetType="Control">
<Setter Property="Background" Value="Orange"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ColumnDataPoint">
<Border Background="Orange"
BorderBrush="Orange"
BorderThickness="0.5">
<Grid Background="{x:Null}">
<ToolTipService.ToolTip>
<StackPanel Orientation="Horizontal">
<ContentControl
Content ="{ TemplateBinding
FormattedDependentValue }"/>
<ContentControl Content =" ppm"/>
</StackPanel>
</ToolTipService.ToolTip>
<Border BorderBrush="{x:Null}">
<Border.Background>
<LinearGradientBrush EndPoint="1.344,0.5"
StartPoint="-0.344,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Orange" Offset="0.5"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</toolkit:ResourceDictionaryCollection>
</toolkit:Chart.Palette>
Note that from the above code, the Chart
is bound to a class but this time around, the colors are fixed, although it is still possible to dynamically change the colors by databinding the Background
properties in the ControlTemplate
.
The class to which the chart is bound is:
public class GasName_Value
{
public string Year { get; set; }
public double GasValue1 { get; set; }
public double GasValue2 { get; set; }
public double GasValue3 { get; set; }
public double GasValue4 { get; set; }
public double GasValue5 { get; set; }
public double GasValue6 { get; set; }
}
The final StackedColumnSeries
looks like this:
These techniques can also be applied to other StackedColumn Series such as Stacked100ColumnSeries
, StakedBarSeries
, and Stacked100BarSeries
, with very little manipulations. Happy Styling!