Edit this page

MVVM Support

The Telerik RadChart control can be used with great success with the Model-View-ViewModel (MVVM) pattern. This tutorial will demonstrate how to use the MVVM pattern to get a better control over the colors of the bars of a single series.

  • Create a new class named Person, which implements the INotifyPropertyChanged interface.

public class Person : INotifyPropertyChanged
{
    private double grade;
    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        private set
        {
            _name = value;
        }
    }
    public double Grade
    {
        get
        {
            return this.grade;
        }
        set
        {
            this.grade = value;
            this.OnPropertyChanged( "Grade" );
        }
    }
    public Person( string name, double grade )
    {
        Name = name;
        Grade = grade;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged( string propertyName )
    {
        if ( this.PropertyChanged != null )
            this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
    }
}
Public Class Person
    Implements INotifyPropertyChanged
    Private m_grade As Double
    Private _name As String

    Public Property Name() As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Property Grade() As Double
        Get
            Return Me.m_grade
        End Get
        Set(ByVal value As Double)
            Me.m_grade = value
            Me.OnPropertyChanged("Grade")
        End Set
    End Property

    Public Sub New(ByVal name__1 As String, ByVal grade__2 As Double)
        Name = name__1
        Grade = grade__2
    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class

The class has two properties:

  • Name - will be displayed on the X axis.

  • Grade - will be displayed on the Y axis.

Create a new class named PersonViewModel, which implements the INotifyPropertyChanged interface. It will be used as an ItemsSource for the chart control.

public class PersonViewModel : INotifyPropertyChanged
{
    private Person _person;
    public Person Person
    {
        get
        {
            return _person;
        }
        private set
        {
            _person = value;
        }
    }
    private Brush _gradeColor;
    public Brush GradeColor
    {
        get
        {
            return _gradeColor;
        }
        private set
        {
            _gradeColor = value;
        }
    }
    public PersonViewModel( Person person )
    {
        this.Person = person;
        person.PropertyChanged += HandleStudentPropertyChanged;
        this.UpdateGradeColor();
    }
    private void HandleStudentPropertyChanged( object sender, PropertyChangedEventArgs e )
    {
        if ( e.PropertyName == "Grade" )
            this.UpdateGradeColor();
    }
    private void UpdateGradeColor()
    {
        if ( this.Person.Grade < 10 )
            this.GradeColor = this.CreateBrush( "#FFFF3D40" );
        else if ( this.Person.Grade < 20 )
            this.GradeColor = this.CreateBrush( "#FFFD583A" );
        else if ( this.Person.Grade < 30 )
            this.GradeColor = this.CreateBrush( "#FFFD8145" );
        else if ( this.Person.Grade < 40 )
            this.GradeColor = this.CreateBrush( "#FFFAA03D" );
        else if ( this.Person.Grade < 50 )
            this.GradeColor = this.CreateBrush( "#FFFAC741" );
        else if ( this.Person.Grade < 60 )
            this.GradeColor = this.CreateBrush( "#FFFCED40" );
        else if ( this.Person.Grade < 70 )
            this.GradeColor = this.CreateBrush( "#FFD7FC3C" );
        else if ( this.Person.Grade < 80 )
            this.GradeColor = this.CreateBrush( "#FF99F839" );
        else if ( this.Person.Grade < 90 )
            this.GradeColor = this.CreateBrush( "#FF46E633" );
        else
            this.GradeColor = this.CreateBrush( "#FF1CD64D" );
        this.OnPropertyChanged( "GradeColor" );
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged( string propertyName )
    {
        if ( this.PropertyChanged != null )
            this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
    }
    private Brush CreateBrush( string color )
    {
        return new SolidColorBrush( this.GetColorFromHexString( color ) );
    }
    private Color GetColorFromHexString( string s )
    {
        s = s.Replace( "#", string.Empty );
        byte a = System.Convert.ToByte( s.Substring( 0, 2 ), 16 );
        byte r = System.Convert.ToByte( s.Substring( 2, 2 ), 16 );
        byte g = System.Convert.ToByte( s.Substring( 4, 2 ), 16 );
        byte b = System.Convert.ToByte( s.Substring( 6, 2 ), 16 );
        return Color.FromArgb( a, r, g, b );
    }
}
Public Class PersonViewModel
    Implements INotifyPropertyChanged
    Private _person As Person
    Public Property Person() As Person
        Get
            Return _person
        End Get
        Private Set(ByVal value As Person)
            _person = value
        End Set
    End Property

    Private _gradeColor As Brush
    Public Property GradeColor() As Brush
        Get
            Return _gradeColor
        End Get
        Private Set(ByVal value As Brush)
            _gradeColor = value
        End Set
    End Property

    Public Sub New(ByVal person As Person)
        Me.Person = person
        AddHandler person.PropertyChanged, AddressOf HandleStudentPropertyChanged
        Me.UpdateGradeColor()
    End Sub

    Private Sub HandleStudentPropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
        If e.PropertyName = "Grade" Then
            Me.UpdateGradeColor()
        End If
    End Sub

    Private Sub UpdateGradeColor()
        If Me.Person.Grade < 10 Then
            Me.GradeColor = Me.CreateBrush("#FFFF3D40")
        ElseIf Me.Person.Grade < 20 Then
            Me.GradeColor = Me.CreateBrush("#FFFD583A")
        ElseIf Me.Person.Grade < 30 Then
            Me.GradeColor = Me.CreateBrush("#FFFD8145")
        ElseIf Me.Person.Grade < 40 Then
            Me.GradeColor = Me.CreateBrush("#FFFAA03D")
        ElseIf Me.Person.Grade < 50 Then
            Me.GradeColor = Me.CreateBrush("#FFFAC741")
        ElseIf Me.Person.Grade < 60 Then
            Me.GradeColor = Me.CreateBrush("#FFFCED40")
        ElseIf Me.Person.Grade < 70 Then
            Me.GradeColor = Me.CreateBrush("#FFD7FC3C")
        ElseIf Me.Person.Grade < 80 Then
            Me.GradeColor = Me.CreateBrush("#FF99F839")
        ElseIf Me.Person.Grade < 90 Then
            Me.GradeColor = Me.CreateBrush("#FF46E633")
        Else
            Me.GradeColor = Me.CreateBrush("#FF1CD64D")
        End If

        Me.OnPropertyChanged("GradeColor")
    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Private Function CreateBrush(ByVal color As String) As Brush
        Return New SolidColorBrush(Me.GetColorFromHexString(color))
    End Function

    Private Function GetColorFromHexString(ByVal s As String) As Color
        s = s.Replace("#", String.Empty)

        Dim a As Byte = System.Convert.ToByte(s.Substring(0, 2), 16)
        Dim r As Byte = System.Convert.ToByte(s.Substring(2, 2), 16)
        Dim g As Byte = System.Convert.ToByte(s.Substring(4, 2), 16)
        Dim b As Byte = System.Convert.ToByte(s.Substring(6, 2), 16)
        Return Color.FromArgb(a, r, g, b)
    End Function

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class

The class has two properties:

  • A reference to a Person object.

  • GradeColor - the corresponding color of the referenced person.

There are a few helper methods. The UpdateGradeColor method is used to return a specific color, based on the person Grade property.

  • Add a new chart declaration to your XAML.

<telerik:RadChart x:Name="radChart" Margin="8"/>
  • The chart will be built in the code-behind.

  • A BarChart will be used.

  • The Grade property will be displayed on the ordinate.

  • The Name property will be displayed on the absciss.

private void MVVM_Loaded( object sender, RoutedEventArgs e )
{
    radChart.DefaultView.ChartArea.AxisY.AutoRange = false;
    radChart.DefaultView.ChartArea.AxisY.MinValue = 0;
    radChart.DefaultView.ChartArea.AxisY.MaxValue = 100;
    radChart.DefaultView.ChartArea.AxisY.Step = 10;
    radChart.DefaultView.ChartLegend.Visibility = System.Windows.Visibility.Collapsed;
    radChart.DefaultSeriesDefinition = new BarSeriesDefinition();

    SeriesMapping seriesMapping = new SeriesMapping();
    seriesMapping.ItemMappings.Add( new ItemMapping( "Person.Grade", DataPointMember.YValue ) );
    seriesMapping.ItemMappings.Add( new ItemMapping( "Person.Name", DataPointMember.XCategory ) );
    radChart.SeriesMappings.Add( seriesMapping );
    radChart.ItemsSource = this.GetData();
}
Private Sub MVVM_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    radChart.DefaultView.ChartArea.AxisY.AutoRange = False
    radChart.DefaultView.ChartArea.AxisY.MinValue = 0
    radChart.DefaultView.ChartArea.AxisY.MaxValue = 100
    radChart.DefaultView.ChartArea.AxisY.[Step] = 10

    radChart.DefaultView.ChartLegend.Visibility = System.Windows.Visibility.Collapsed
    radChart.DefaultSeriesDefinition = New BarSeriesDefinition()

    Dim seriesMapping As New SeriesMapping()
    seriesMapping.ItemMappings.Add(New ItemMapping("Person.Grade", DataPointMember.YValue))
    seriesMapping.ItemMappings.Add(New ItemMapping("Person.Name", DataPointMember.XCategory))

    radChart.SeriesMappings.Add(seriesMapping)
    radChart.ItemsSource = Me.GetData()
End Sub

Here is the code for the GetData() method, which creates the mockup data:

private List<PersonViewModel> GetData()
{
    Random rand = new Random( DateTime.Now.Millisecond );
    List<Person> studentList = new List<Person>();
    for ( int i = 0; i < rand.Next( 9, 12 ); i++ )
        studentList.Add( new Person( "Person" + i, rand.Next( 15, 100 ) ) );
    List<PersonViewModel> modelList = new List<PersonViewModel>();
    foreach ( Person person in studentList )
        modelList.Add( new PersonViewModel( person ) );
    return modelList;
}
Private Function GetData() As List(Of PersonViewModel)
    Dim rand As New Random(DateTime.Now.Millisecond)
    Dim studentList As New List(Of Person)()

    For i As Integer = 0 To rand.Next - 1
        studentList.Add(New Person("Person" & i, rand.Next))
    Next

    Dim modelList As New List(Of PersonViewModel)()
    For Each person As Person In studentList
        modelList.Add(New PersonViewModel(person))
    Next

    Return modelList
End Function

The result so far can be seen on the image below:

  • The final step is to bind the GradeColor property of the PersonViewModel business object to the background of a single bar.

In order to change the background of each bar from the chart, you need to use the CreateItemStyleDelegate property of the RadChart. For more information read here.

Here is the method that will be passed to the delegate.

public Style BuildCustomItemStyle( Control item, Style style, DataPoint point, DataSeries dataSeries )
{
    if ( ( item as BaseChartItem ) == null )
    {
        return style;
    }
    Style newStyle = new Style();
    newStyle.BasedOn = style;
    newStyle.TargetType = typeof( Shape );
    Brush brush = ( dataSeries[ ( item as BaseChartItem ).CurrentIndex ].DataItem as PersonViewModel ).GradeColor;
    newStyle.Setters.Add( new Setter( Shape.FillProperty, brush ) );
    return newStyle;
}
Public Function BuildCustomItemStyle(item As Control, style As Style, point As DataPoint, dataSeries As DataSeries) As Style
    If TryCast(item, BaseChartItem) Is Nothing Then
        Return style
    End If
    Dim newStyle As New Style()
    newStyle.BasedOn = style
    newStyle.TargetType = GetType(Shape)
    Dim brush As Brush = TryCast(dataSeries(TryCast(item, BaseChartItem).CurrentIndex).DataItem, PersonViewModel).GradeColor
    newStyle.Setters.Add(New Setter(Shape.FillProperty, brush))
    Return newStyle
End Function

To pass this method to the RadChart use its CustomItemStyleDelegate property.

this.radChart.CreateItemStyleDelegate = this.BuildCustomItemStyle;
Me.radChart.CreateItemStyleDelegate = Me.BuildCustomItemStyle

Set this property into the MVVM_Loaded handler.

Here is the final result:

Additionally you may want to remove the Background from the SeriesItemsLabels. This can be achieved by retemplating the default Style so that the Background applied to the Labels is Transparent and setting the Foreground to Black for example:

<Style x:Key="MySeriesItemLabel_Style" TargetType="telerik:SeriesItemLabel">
    <Setter Property="Padding" Value="2,0" />
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="IsHitTestVisible" Value="False"/>
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content }" TextAlignment="Center" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template" >
        <Setter.Value>
            <ControlTemplate TargetType="telerik:SeriesItemLabel">
                <Canvas x:Name="PART_MainContainer">
                    <Path                            
                  Visibility="{TemplateBinding ConnectorVisibility}"
                  Style="{TemplateBinding ConnectorStyle}"
                  Stroke="{TemplateBinding Stroke}"
                  StrokeThickness="{TemplateBinding StrokeThickness}">
                        <Path.Data>
                            <PathGeometry >
                                <PathGeometry.Figures>
                                    <PathFigure x:Name="PART_Connector">
                                        <PathFigure.Segments>
                                            <PolyLineSegment />
                                        </PathFigure.Segments>
                                    </PathFigure>
                                </PathGeometry.Figures>
                            </PathGeometry>
                        </Path.Data>
                    </Path>
                    <Border x:Name="PART_TextContainer"
                   Style="{TemplateBinding LabelStyle}"
                   BorderBrush="{TemplateBinding Stroke}"
                   Background="Transparent"
                   Width="{TemplateBinding Width}"
                   Height="{TemplateBinding Height}">
                        <ContentPresenter Margin="{TemplateBinding Padding}" />
                    </Border>
                </Canvas>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Don't forget to apply the Style to the SeriesItemLabelStyle property of the Series Definition:

radChart.DefaultSeriesDefinition.SeriesItemLabelStyle = this.Resources["MySeriesItemLabel_Style"] as Style;
radChart.DefaultSeriesDefinition.SeriesItemLabelStyle = TryCast(Me.Resources("MySeriesItemLabel_Style"), Style)

The result:

See Also

Was this article helpful? Yes No

Give article feedback

Tell us how we can improve this article

Dummy