I have followed the MVVM Chart example and have it working great in my own project. I am now trying to load the chart with my data rather than the hard-coded data in the example. My problem though is that I am using WCF services which must be called asynch so within my viewmodel the Data property is being accessed before the asynch callback has occured and there is nothing in my data object.
Imports Telerik.Windows.Controls Public Class ExampleViewModel Private _costIndexData As List(Of Dictionary(Of String, String)) Public Property TinNumber As String Public Property SubTinSequenceNumber As String Public Sub New() BeginRequest() End Sub Private Sub BeginRequest() Dim lProviderService As New ProviderService.ProviderDashboardServiceClient() AddHandler lProviderService.GetCostIndexSummaryDataCompleted, AddressOf CostIndexSummaryDataCompletedCallback lProviderService.GetCostIndexSummaryDataAsync(TinNumber, SubTinSequenceNumber) End Sub Private Sub CostIndexSummaryDataCompletedCallback(ByVal sender As Object, ByVal e As ProviderService.GetCostIndexSummaryDataCompletedEventArgs) _costIndexData = e.Result End Sub Public ReadOnly Property Data() As List(Of DataViewModel) Get Dim dataList As New List(Of Data)()
For Each lRecord As Dictionary(Of String, String) In _costIndexData dataList.Add(New Data(lRecord("ServiceCategory"), lRecord("CostIndex"))) Next Dim modelList As New List(Of DataViewModel)() For Each dataItem As Data In dataList modelList.Add(New DataViewModel(dataItem)) Next Return modelList End Get End Property Public ReadOnly Property ApplicationThemeAwareForeground() As Brush Get If Not StyleManager.ApplicationTheme Is Nothing _ AndAlso StyleManager.ApplicationTheme.[GetType]() Is GetType(Expression_DarkTheme) Then Return New SolidColorBrush(Color.FromArgb(255, 204, 204, 204)) End If Return New SolidColorBrush(Color.FromArgb(255, 0, 0, 0)) End Get End Property End ClassHow do I go about getting the data from my WCF service loaded before the Data property is accessed? Also, you will notice I have two public properties which are needed to actually get my data. How do I go about passing those values in to my viewmodel?
Thank you!
11 Answers, 1 is accepted
The reason why the RadChart doesn't show the new data is because it cannot know when its data source has changed. You can use the PropertyChanged event, provided by the INotifyPropertyChanged interface, to notify the framework that the new data has arrived and it must re-read it. You can convert your _costIndexData field to a property that throws this event, and RadChart will rebind itself automatically. Your property can look like this:
Public Property CostIndexData() As List(Of Dictionary(Of String, String)) Get Return _costIndexData End Get Set(ByVal value As List(Of Dictionary(Of String, String))) _costIndexData = value ' Call NotifyPropertyChanged when the property is updated NotifyPropertyChanged("CostIndexData") End SetEnd PropertyNow when you want to set the new data to your RadChart use this new property instead of the backing field. Also make sure you are binding your ItemsSource property of your chart to the new notifiable property.
Hope this helps!
Yavor Ivanov
the Telerik team
Yes that definitely helps but now I am getting the attached error when trying to view my MainPage.xaml file in the designer.
Data.vb:
Imports System.ComponentModel Public Class Data Implements INotifyPropertyChanged Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private _serviceCategory As String Private _costIndex As Double Public Sub New(ByVal serviceCategory As String, ByVal costIndex As Double) Me._serviceCategory = serviceCategory Me._costIndex = costIndex End Sub Public Property ServiceCategory() As String Get Return Me._serviceCategory End Get Set(ByVal value As String) If String.Compare(Me._serviceCategory, Value, StringComparison.CurrentCultureIgnoreCase) = 0 Then Return End If Me._serviceCategory = Value End Set End Property Public Property CostIndex() As Double Get Return Me._costIndex End Get Set(ByVal value As Double) If Me._costIndex = Value Then Return End If Me._costIndex = Value End Set End Property Protected Overridable Sub OnPropertyChanged(propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End ClassDataViewModel.vb
Imports System.ComponentModel Imports System.Windows.Media Public Class DataViewModel Implements INotifyPropertyChanged Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Private _data As Data Private _populationColor As Brush Public Sub New(data As Data) Me._data = data AddHandler data.PropertyChanged, AddressOf HandleDataPropertyChanged Me.UpdatePopulationColor() End Sub Public ReadOnly Property Data() As Data Get Return _data End Get End Property Public Property PopulationColor() As Brush Get Return _populationColor End Get Private Set If Object.Equals(Me._populationColor, value) Then Return End If Me._populationColor = value Me.OnPropertyChanged("PopulationColor") End Set End Property Private Sub HandleDataPropertyChanged(sender As Object, e As PropertyChangedEventArgs) If e.PropertyName = "Population" Then Me.UpdatePopulationColor() End If End Sub Protected Overridable Sub OnPropertyChanged(propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Private Function CreateBrush(color As String) As Brush Return New SolidColorBrush(Me.GetColorFromHexString(color)) End Function Private Function GetColorFromHexString(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 Private Sub UpdatePopulationColor() If Me.Data.CostIndex < 1 Then Me.PopulationColor = Me.CreateBrush("#00CD00") Else Me.PopulationColor = Me.CreateBrush("#EE0000") End If End Sub End ClassExampleViewModel.vb
#Region "Imports" Imports System.ComponentModel #End Region Public Class ExampleViewModel Implements INotifyPropertyChanged #Region "Public Events" Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged #End Region Private _costIndexData As List(Of DataViewModel) #Region "Public Properties" Public Property TinNumber As String Public Property SubTinSequenceNumber As String #End Region Public Sub New() BeginRequest() End Sub Public Property CostIndexData As List(OF DataViewModel) Get Return _costIndexData End Get Set(ByVal value As List(Of DataViewModel)) _costIndexData = value ' Call NotifyPropertyChanged when the property is updated NotifyPropertyChanged("CostIndexData") End Set End Property Private Sub BeginRequest() Dim lProviderService As New ProviderService.ProviderDashboardServiceClient() AddHandler lProviderService.GetCostIndexSummaryDataCompleted, AddressOf CostIndexSummaryDataCompletedCallback lProviderService.GetCostIndexSummaryDataAsync(TinNumber, SubTinSequenceNumber) End Sub Private Sub NotifyPropertyChanged(ByVal info As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info)) End Sub Private Sub CostIndexSummaryDataCompletedCallback(ByVal sender As Object, ByVal e As ProviderService.GetCostIndexSummaryDataCompletedEventArgs) Dim lRecords As List(Of Dictionary(Of String, String)) = e.Result Dim lDatalist As New List(Of Data) For Each lRecord As Dictionary(Of String, String) In lRecords lDatalist.Add(New Data(lRecord("ServiceCategory").ToString.Trim, lRecord("CostIndex"))) Next Dim lModelList As New List(Of DataViewModel) For Each lDataItem As Data In lDatalist lModelList.Add(New DataViewModel(lDataItem)) Next CostIndexData = lModelList End Sub End ClassMainPage.xaml
<UserControl x:Class="Ebms.Silverlight.Controls.MainPage" xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" xmlns:Example="clr-namespace:MyCompany.Silverlight.Controls"> <UserControl.DataContext> <Example:ExampleViewModel /> </UserControl.DataContext> <UserControl.Resources> <Style x:Key="CustomStyle" TargetType="telerik:Bar"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="telerik:Bar"> <Canvas> <Rectangle x:Name="PART_DefiningGeometry" Height="{TemplateBinding ItemActualHeight}" Width="{TemplateBinding ItemActualWidth}" Fill="{Binding DataItem.PopulationColor}" /> <Canvas.RenderTransform> <ScaleTransform x:Name="PART_AnimationTransform" ScaleY="0" /> </Canvas.RenderTransform> </Canvas> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="CustomLabelStyle" TargetType="telerik:SeriesItemLabel"> <Setter Property="HorizontalContentAlignment" Value="Center" /> <Setter Property="Padding" Value="2,0" /> <Setter Property="IsHitTestVisible" Value="False"/> <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="Transparent" Background="Transparent"> <TextBlock Foreground="Black" TextAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0" Text="{TemplateBinding Content}" /> </Border> </Canvas> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <telerik:RadChart x:Name="RadChart1" ItemsSource="{Binding CostIndexData}"> <telerik:RadChart.SeriesMappings> <telerik:SeriesMapping> <telerik:SeriesMapping.SeriesDefinition> <telerik:BarSeriesDefinition ItemStyle="{StaticResource CustomStyle}" SeriesItemLabelStyle="{StaticResource CustomLabelStyle}" ItemLabelFormat="#Y{0.}"> <telerik:BarSeriesDefinition.LabelSettings> <telerik:BarLabelSettings LabelDisplayMode="MidPoint" Distance="0" /> </telerik:BarSeriesDefinition.LabelSettings> </telerik:BarSeriesDefinition> </telerik:SeriesMapping.SeriesDefinition> <telerik:ItemMapping FieldName="Data.CostIndex" DataPointMember="YValue" /> <telerik:ItemMapping FieldName="Data.ServiceCategory" DataPointMember="XCategory" /> </telerik:SeriesMapping> </telerik:RadChart.SeriesMappings> <telerik:RadChart.DefaultView> <telerik:ChartDefaultView ChartLegendPosition="Bottom"> <telerik:ChartDefaultView.ChartTitle> <telerik:ChartTitle Content="Population change (average annual change, thousand)" /> </telerik:ChartDefaultView.ChartTitle> <telerik:ChartDefaultView.ChartArea> <telerik:ChartArea> <telerik:ChartArea.AxisX> <telerik:AxisX MajorTicksVisibility="Collapsed" /> </telerik:ChartArea.AxisX> <telerik:ChartArea.AxisY> <telerik:AxisY AutoRange="False" MinValue="-7000" MaxValue="7000" Step="1000" MinorTicksVisibility="Collapsed" MajorTicksVisibility="Collapsed" /> </telerik:ChartArea.AxisY> </telerik:ChartArea> </telerik:ChartDefaultView.ChartArea> </telerik:ChartDefaultView> </telerik:RadChart.DefaultView> </telerik:RadChart> </Grid> </UserControl> The error is pointing to the line:
Dim
lRecords As List(Of Dictionary(Of String, String
In the CostIndexSummaryDataCompletedCallback method of the ExampleViewModel class.
I know I must be doing something wrong but I can't tell what. Help!!
Thanks!
Thank you!
Thank you!
It seems that the problem that you are experiencing is not connected directly to telerik products, but rather to a problem in VS when the designer is trying to connect to a web service. The solution is to check if the code is running under the context of a designer and if so not to connect to the web service.
I have attached a sample application that demonstrates how you can find out if the code is running under the context of a designer to change the background of the page.
Yavor Ivanov
the Telerik team
Thanks again, I will let you know if this helps or not.
Can you please change the constructor of your ExampleViewModel so that it doesn't call BeginRequest, but instead initializes your data using sample data. If the designer doesn't crash then I am afraid that the problem is Visual Studio related. In such case its best that you look for solution at MSDN and user communities.
Greetings,Yavor Ivanov
the Telerik team
Thank you, I will try. To clarify though it didn't start happening until I hooked it up to the WCF Service. It also doesn't happen on the gauge controls and those are hoooked up to WCF (and i'm not even detecting the environment). I do think that vs 2010 is incredibly buggy, I have had so many problems with it I am longing for vs 2008.
Alternatively you can try using Telerik RadDomainDataSource which can make your life easier when using WCF RIA services. This blog post can be useful as it contains a sample service, code and step by step explanations.
Hope this helps!
Yavor Ivanov
the Telerik team