TreeMap in Silverlight Toolkit: How to write your own interpolator
One of the new concepts introduced in the TreeMap control are Interpolators. They are used for example to interpolate the colors of rectangles or the font size in the titles of the NHL teams in the sample TreeMap in the picture above. I would like to show how easy it is to create your new custom interpolator. Here is how we used interpolators in the sample above:
<datavis:TreeMap x:Name="treeMapControl" Grid.Row="1">
<datavis:TreeMap.Interpolators>
< datavis : SolidColorBrushInterpolator TargetName ="itemBorder" TargetProperty ="Background"
DataRangeBinding="{Binding Losses}" From="Green" To="Red" />
< datavis : DoubleInterpolator TargetName ="textBlk" TargetProperty ="FontSize"
DataRangeBinding="{Binding GoalsFor}" From="8" To="15" />
</datavis:TreeMap.Interpolators>
<datavis:TreeMap.ItemDefinition>
<datavis:TreeMapItemDefinition ItemsSource="{Binding Children}" ValueBinding="{Binding Points}" ChildItemPadding="1">
<DataTemplate>
<Border x:Name="itemBorder" BorderBrush="Black" BorderThickness="1" ToolTipService.ToolTip="{Binding ToolTip}">
<TextBlock x:Name="textBlk" Foreground="White" Text="{Binding Name}" VerticalAlignment="Center" Margin="2,2,0,0"
TextWrapping="Wrap" TextAlignment="Center"/>
</Border>
</DataTemplate>
</datavis:TreeMapItemDefinition>
</datavis:TreeMap.ItemDefinition>
</datavis:TreeMap>
Now let’s say that we don’t like the color transitions and we would like to createour own interpolator to provide much more natural color transitions than going from green to red thru brown. No problem – let’s use HSL instead of the regular RGB color interpolation. All you need to do in order to introduce your new interpolator is to inherit from RangeInterpolator<Color> - abstract class delivered with the TreeMap control and implement object Interpolate(double value) method:
namespace System.Windows.Controls.Samples
{
/// <summary>
/// Interpolator which converts a numeric value from its [ActualDataMinimum, ActualDataMaximum]
/// range to a color in the range [From, To].
/// </summary>
/// <QualityBand>Experimental</QualityBand>
public class HSLSolidColorBrushInterpolator : RangeInterpolator<Color>
{
/// <summary>
/// Interpolates the given value between its [ActualDataMinimum, ActualDataMaximum] range
/// and returns a color in the range [From, To].
/// </summary>
/// <param name="value">Value to interpolate.</param>
/// <returns>An interpolated color in the range [From, To].</returns>
public override object Interpolate(double value)
{
Color color = From;
if (DataMaximum - DataMinimum != 0)
{
double ratio = (value - ActualDataMinimum) / (ActualDataMaximum - ActualDataMinimum);
color = color.FromAhsl(
(byte)((double)From.A + (ratio * (double)(To.A - From.A))),
From.GetHue() + (ratio * (To.GetHue() - From.GetHue())),
From.GetSaturation() + (ratio * (To.GetSaturation() - From.GetSaturation())),
From.GetLightness() + (ratio * (To.GetLightness() - From.GetLightness())));
}
return new SolidColorBrush(color);
}
}
}
Here is how it works: Interpolate method is called for each node with the actual value we interpolate on. Before it happens ActualDataMinimum and ActualDataMaximum are pre-calculated on the all leaf nodes (by default) or all nodes (if we choose InterpolationMode="AllNodes" as an attribute in the interpolator).
There is one more helper class with actual extension methods for RGB->HSL and HSL-> RGB conversions. It is included in the attachment to this post.
Now everything we have to do is to change the XAML:
<Samples:HSLSolidColorBrushInterpolator TargetName="itemBorder" TargetProperty="Background"
DataRangeBinding="{Binding Losses}" From="Green" To="Red" />
The result is like this:
You may ask why the HSL interpolator wasn’t chosen as the default one for Colors in the TreeMap. Two reasons:
· Sometimes color transitions are counterintuitive with HSL (like with RGB presented here)
· There is no native support in Blend for HSL interpolation
We may however consider adding this interpolator as optional in the next release to the default SolidColorBrushInterpolator.