Using a XamDataChart to Create a Series of Items with Different Colors, Depending on the Values

[Infragistics] Mihail Mateev / Thursday, August 26, 2010

Infragistics XamDataChart for Silverlight and WPF proposes a flexible approach to create a series where markers look different depending of properties of the bound items.

It is possible to use a MarkerTemplate property of all kind of MarkerSeries like ScatterSeries, RangeColumnSeries.
There it is possible to create own DataTemplate where properties of the UI elements could be bound to the properties of the item DataContext.

<DataTemplate x:Key="bubbleTemplate" >

    <Ellipse Stretch="Fill"

               HorizontalAlignment="Stretch"

               VerticalAlignment="Stretch"

               StrokeThickness="0.5"

               MinWidth="10" MinHeight="10"

               Width="{Binding Item.Width}"

               Height="{Binding Item.Width}">

        <ToolTipService.ToolTip>

            <StackPanel Orientation="Vertical">

                <TextBlock Text="{Binding Item.YValue, StringFormat='Value: {0}'}" />

            </StackPanel>

        </ToolTipService.ToolTip>

    </Ellipse>

</DataTemplate>

 

An appropriate sample could be found there: http://help.infragistics.com/Help/NetAdvantage/DV/2010.2/CLR4.0/html/xamDataChart_Marker_Values.html

 

Sometimes developers want to change the color of the marker, depending of the some values in the item DataContext. Values could be from different types. To be possible to convert a value from specified type to Brush with a specific color we need to have a ValueConverter.

This article demonstrates two different converters: first - from a number to discrete color palette and a second - from number to continuous color palette.

Sample application is built for Silverlight. It is possible to use the same approach to create a WPF application with XamDataChart for WPF

Requirements to build a sample application:

Software:

  • Visual Studio 2010
  • Infragistics XamMap component: NetAdvantage for Silverlight Data Visualization 2010 vol.2

http://www.infragistics.com/dotnet/netadvantage/silverlight/data-visualization.aspx#Downloads

Steps to reproduce:

 For Silverlight Demo:

  • Create a Silverlight application.
  • Create a ValueConverters -  NumberToBrush to convert number to Brush with color from a discrete palette and RgbValueConverter (in the most components Brush is the type, used for Background, Stroke, etc) to convert a number to Brush with color from a continuous palette.
  • Create a sample data, used like a DataContext for XamDataChart.
  • Create a MarkerTemplates, representing ellipsis where color is bound to property YValue, using both converters.
  • Create a XamDataCharts with a scatter series, using created MarkerTemplate definitions.
  • Run the sample application

1. Create a Silverlight Application with Visual Studio 2010.

2. Add a two ValueConverters:

NumberToBrush to convert a number (from type double) to SolidColorBrush.
Brush depends on the color that is taken from a discrete palette. In a palette of six SolidColorBrishes with colors - white, red, orange, yellow, green and blue. As a parameter is set to Range Min and Max values.  Converter returns a SolidColorBrush, which corresponds to the number (space is divided into 5). If the value is outside the range of returns SolidColorBrush with white color.

public sealed class NumberToBrush : IValueConverter

{

 

    private Brush[] _brushes = new Brush[] { new SolidColorBrush(Colors.White),

                                            new SolidColorBrush(Colors.Red),

                                            new SolidColorBrush(Colors.Orange),

                                            new SolidColorBrush(Colors.Yellow),

                                            new SolidColorBrush(Colors.Green),

                                            new SolidColorBrush(Colors.Blue)};

 

    #region Property Accessors

 

    public Brush[] Brushes

    {

        get { return this._brushes; }

        set { this._brushes = value; }

    }

 

    #endregion

 

    #region IValueConverter

 

    public object Convert(object value, Type targetType, object parameter,

        System.Globalization.CultureInfo culture)

    {

 

        if ((typeof(Brush) != targetType || typeof(double) != value.GetType()))

        {

            return null;

        }

 

        Range range = parameter as Range;

        if(range == null)

        {

            range = new Range();

            range.Min = 0;

            range.Max = 10;

        }

 

        var i = (double)value;

 

        double interval = (double)(range.Max - range.Min)/(_brushes.Length - 1);

        int index = (int)(i / interval + 0.5);

 

        if (index <= 0 || index >= this._brushes.Length) return this._brushes[0];

 

        return this._brushes[index];

    }

 

    public object ConvertBack(object value, Type targetType, object parameter,

        System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

    #endregion

}

 

public class Range : BaseViewModel

{

    #region Min

 

    private int _min;

    public int Min

    {

        get { return this._min; }

        set

        {

            this._min = value;

            OnPropertyChanged("Min");

        }

    }

 

    #endregion // Min

 

    #region Max

 

    private int _max;

    public int Max

    {

        get { return this._max; }

        set

        {

            this._max = value;

            OnPropertyChanged("Max");

        }

    }

 

    #endregion // Max

 

}

RgbValueConverter converts number to SolidColorBrush form color of continuous palette.

Converter uses HSV (Hue-Saturation-Value)

http://en.wikipedia.org/wiki/HSL_and_HSV

Form Wikipedia:

HSL and HSV are the two most common cylindrical-coordinate representations of points in an RGB color model, which rearrange the geometry of RGB in an attempt to be more perceptually relevant than the cartesian representation. They were developed in the 1970s for computer graphics applications, and are used for color pickers, in color-modification tools in image editing software

HSL and HSV are both cylindrical geometries  with hue, their angular dimension, starting at the red primary at 0°, passing through the green primary at 120° and the blue primary at 240°, and then wrapping back to red at 360°.

HSV is a different way of representing a color (as opposed to RGB, where R indicates a value from 0 to 255 that "controls the amount of red", G for green and B for Blue). Have a look at the link above for an in-depth discussion.

In the RgbValueConverter number value is normalized to fit between Min and Max values :

var normValue = i * 360/(range.Max - range.Min);

There is the whole code for RgbValueConverter:

public sealed class RgbValueConverter : IValueConverter

{

 

    private static Color ColorFromHsv(double hue, double saturation, double value)

    {

        int hi = (int)(Math.Floor(hue / 60)) % 6;

        double f = hue / 60 - Math.Floor(hue / 60);

 

        value = value * 255;

        int v = (int)(value);

        int p = (int)(value * (1 - saturation));

        int q = (int)(value * (1 - f * saturation));

        int t = (int)(value * (1 - (1 - f) * saturation));

 

        if (hi == 0)

        {

            return Color.FromArgb(255, (byte)v, (byte)t, (byte)p);

        }

 

        if (hi == 1)

        {

            return Color.FromArgb(255, (byte)q, (byte)v, (byte)p);

        }

 

        if (hi == 2)

        {

            return Color.FromArgb(255, (byte)p, (byte)v, (byte)t);

        }

 

        if (hi == 3)

        {

            return Color.FromArgb(255, (byte)p, (byte)q, (byte)v);

        }

 

        if (hi == 4)

        {

            return Color.FromArgb(255, (byte)t, (byte)p, (byte)v);

        }

 

        return Color.FromArgb(255, (byte)v, (byte)p, (byte)q);

    }

 

 

    #region IValueConverter

 

    public object Convert(object value, Type targetType, object parameter,

        System.Globalization.CultureInfo culture)

    {

 

        if ((typeof(Brush) != targetType || typeof(double) != value.GetType()))

        {

            return null;

        }

 

        Range range = parameter as Range;

        if(range == null)

        {

            range = new Range();

            range.Min = 0;

            range.Max = 10;

        }

 

        var i = System.Convert.ToDouble(value);

 

        if (i <= range.Min || range.Min >= range.Max)

        {               

            return new SolidColorBrush(Colors.White);

        }

 

        var normValue = i * 360/(range.Max - range.Min);

 

        return new SolidColorBrush(ColorFromHsv(normValue, 1, 1));

 

    }

 

    public object ConvertBack(object value, Type targetType, object parameter,

        System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

    #endregion

 

}

 

3. Create a sample data, used like a DataContext for XamDataChart.

public class BubbleDataCollection

        : ObservableCollection<BubblePoint>

{

 

    public BubbleDataCollection()

    {

        this.Add(new BubblePoint { XValue = 4, YValue = 10, Width = 30 });

        this.Add(new BubblePoint { XValue = 4, YValue = 4, Width = 40 });

        this.Add(new BubblePoint { XValue = 8, YValue = 8, Width = 20 });

        this.Add(new BubblePoint { XValue = 10, YValue = 1, Width = 50 });

        this.Add(new BubblePoint { XValue = 1, YValue = 10, Width = 40 });

    }

 

}

 

public class BubblePoint

{

    public double XValue { get; set; }

    public double YValue { get; set; }

    public double Width { get; set; }

}

 

    <UserControl.Resources>

        ...

        <XamDataChartCustomTemplatesDemo:BubbleDataCollection x:Key="bubbleCollection" />

       ....

    <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource bubbleCollection}">

4. Create a MarkerTemplates, representing ellipsis where color is bound to property YValue, using both converters.

<UserControl.Resources>

    <Converters:NumberToBrush x:Key="indexToBrush"/>

    <Converters:RgbValueConverter x:Key="RgbToBrush"/>

    <XamDataChartCustomTemplatesDemo:BubbleDataCollection x:Key="bubbleCollection" />

    <Converters:Range x:Key="range" Min="0" Max="10"/>

    <DataTemplate x:Key="bubbleTemplate" >

        <Ellipse Stretch="Fill"

                   HorizontalAlignment="Stretch"

                   VerticalAlignment="Stretch"

                   Fill="{Binding Item.YValue, Converter={StaticResource indexToBrush}, ConverterParameter={StaticResource range}}"

                   Stroke="{Binding Series.ActualMarkerOutline}"

                   StrokeThickness="0.5"

                   MinWidth="10" MinHeight="10"

                   Width="{Binding Item.Width}"

                   Height="{Binding Item.Width}">

            <ToolTipService.ToolTip>

                <StackPanel Orientation="Vertical">

                    <TextBlock Text="{Binding Item.YValue, StringFormat='Value: {0}'}" />

                </StackPanel>

            </ToolTipService.ToolTip>

        </Ellipse>

    </DataTemplate>

    <DataTemplate x:Key="bubbleTemplate2" >

        <Ellipse Stretch="Fill"

                   HorizontalAlignment="Stretch"

                   VerticalAlignment="Stretch"

                   Fill="{Binding Item.YValue, Converter={StaticResource RgbToBrush}, ConverterParameter={StaticResource range}}"

                   Stroke="{Binding Series.ActualMarkerOutline}"

                   StrokeThickness="0.5"

                   MinWidth="10" MinHeight="10"

                   Width="{Binding Item.Width}"

                   Height="{Binding Item.Width}">

            <ToolTipService.ToolTip>

                <StackPanel Orientation="Vertical">

                    <TextBlock Text="{Binding Item.YValue, StringFormat='Value: {0}'}" />

                </StackPanel>

            </ToolTipService.ToolTip>

        </Ellipse>

    </DataTemplate>       

</UserControl.Resources>

MarkerTemplate definitions contain Ellipsis, filled with a Brush, depending on YValue of the BubblePoint objects.

5. Create a XamDataCharts with a scatter series, using created MarkerTemplate definitions.

<igChart:XamDataChart x:Name="theChart">

    <igChart:XamDataChart.Axes>

        <igChart:NumericXAxis x:Name="xAxis"

                               MinimumValue="0" MaximumValue="15"/>

        <igChart:NumericYAxis x:Name="yAxis"

                               MinimumValue="0" MaximumValue="15"/>

    </igChart:XamDataChart.Axes>

 

    <igChart:XamDataChart.Series>

        <igChart:ScatterSeries x:Name="scatter"

                               MarkerTemplate="{StaticResource bubbleTemplate}"

                               ItemsSource="{Binding}"

                               XMemberPath="XValue"

                               YMemberPath="YValue"

                               XAxis="{Binding ElementName=xAxis}"

                               YAxis="{Binding ElementName=yAxis}"></igChart:ScatterSeries>

    </igChart:XamDataChart.Series>

</igChart:XamDataChart>

...

<igChart:XamDataChart x:Name="theChart2">

    <igChart:XamDataChart.Axes>

        <igChart:NumericXAxis x:Name="xAxis2"

                                   MinimumValue="0" MaximumValue="15"/>

        <igChart:NumericYAxis x:Name="yAxis2"

                                   MinimumValue="0" MaximumValue="15"/>

    </igChart:XamDataChart.Axes>

 

    <igChart:XamDataChart.Series>

        <igChart:ScatterSeries x:Name="scatter2"

                                   MarkerTemplate="{StaticResource bubbleTemplate2}"

                                   ItemsSource="{Binding}"

                                   XMemberPath="XValue"

                                   YMemberPath="YValue"

                                   XAxis="{Binding ElementName=xAxis2}"

                                   YAxis="{Binding ElementName=yAxis2}"></igChart:ScatterSeries>

    </igChart:XamDataChart.Series>

 

</igChart:XamDataChart>

 

6. Run the application:

 

Sample code you could find here: