This is a migrated thread and some comments may be shown as answers.

[Solved] MVVM Example with WCF Services

11 Answers 163 Views
Chart
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
jfkrueger
Top achievements
Rank 1
jfkrueger asked on 26 May 2011, 05:24 PM

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 Class

How 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

Sort by
0
Yavor
Telerik team
answered on 31 May 2011, 08:20 AM
Hi jfkrueger,

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 Set
End Property

Now 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!


Greetings,
Yavor Ivanov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
jfkrueger
Top achievements
Rank 1
answered on 03 Jun 2011, 08:14 PM
Thank you.

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 Class

DataViewModel.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 Class


ExampleViewModel.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 Class

MainPage.xaml

<UserControl x:Class="Ebms.Silverlight.Controls.MainPage"
        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

)) = e.Result

In the CostIndexSummaryDataCompletedCallback method of the ExampleViewModel class.

I know I must be doing something wrong but I can't tell what. Help!!

Thanks!

 

 

0
jfkrueger
Top achievements
Rank 1
answered on 03 Jun 2011, 08:41 PM
It appears that the error only occurs in the designer, which also causes my visual studio 2010 to crash if left open for any amount of time. It looks like the data is binding correctly though which is awesome! My only question now is how to show the values as decimals rather than whole numbers and how to get rid of that error in the designer.

Thank you!
0
jfkrueger
Top achievements
Rank 1
answered on 06 Jun 2011, 05:11 PM
Okay I believe I have everything figured out but the error in the designer is killing me. I can't even work on the xaml anymore because if I have it open for more than 30 seconds visual studio crashes and I have to reopen my entire solution. Could this be beacuse I am calling WCF services on another machine? I have it all working when it runs but the designer keeps crashing.

Thank you!
0
Yavor
Telerik team
answered on 08 Jun 2011, 10:28 AM
Hello jfkrueger,

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.

All the best,
Yavor Ivanov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
jfkrueger
Top achievements
Rank 1
answered on 08 Jun 2011, 03:03 PM
I agree, it's a Visual Studio problem. I will try what you have suggested and let you know if that helps. I have already updated my options though to "Always show full XAML", so the designer never shows anymore, yet it still crashes constantly. Is the designer still being loaded in the background or something?

Thanks again, I will let you know if this helps or not.
0
jfkrueger
Top achievements
Rank 1
answered on 08 Jun 2011, 03:10 PM
I did what was suggested, opened up the designer and the error is no longer there. Then Visual Studio crashed again after about 30 seconds.
0
Yavor
Telerik team
answered on 13 Jun 2011, 12:03 PM
Hello,

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
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
jfkrueger
Top achievements
Rank 1
answered on 15 Jun 2011, 12:18 AM
Hi,

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.
0
Yavor
Telerik team
answered on 17 Jun 2011, 07:35 AM
Hello jfkrueger,

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!

Greetings,
Yavor Ivanov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
jfkrueger
Top achievements
Rank 1
answered on 27 Jun 2011, 08:50 PM
Thanks, I'll have to look into WCF RIA services. For this project I already have a ton of WCF services deployed that gave me exactly what I needed and didn't really want to recreate everything just for a silverlight app.
Tags
Chart
Asked by
jfkrueger
Top achievements
Rank 1
Answers by
Yavor
Telerik team
jfkrueger
Top achievements
Rank 1
Share this question
or