It is written in VB targeting framework 4.5 and later.
Start by creating the WPF project. It will contain an Application, a MainWindow, and eventually a reference to the WCF endpoint.
We will create a data model that is a subclass of a base class. For example, you might have a Transaction base class and JournalEntry, PurchaseOrder, and PayVoucher subclasses. In our project we will have a Thing class and a Person class. We will also add a method and an event to the Person class.
We have to put the model classes in the WCF project. If we put them in the WPF project we will end up with circular references which are not allowed.
At this point we need to add the WCF project. Right-click on the Solution in the Solution Explorer, click Add, then select New Project...
The new project will be a WCF Service Application and will be named "WCFPerson".
In the Solution Explorer rename IService1.vb to IPerson.vb and Service1.svc to Person.svc. In Person.svc.vb rename Class Service1 to PersonService. You can leave the old names in place if you prefer, but they are not a useful names.
Now let's define our Thing and Person classes. Let's start by adding a new class to WCFPerson called Thing. This class will contain a single string property called Name (because everything has a name) that is decorated as a DataMember and will implement NotifyPropertyChanged. All derived classes will be able to use this property and interface. The DataContract and DataMember decorations mean we can transfer objects of this class between WPF and WCF. The implementation of INotifyPropertyChanged means we can bind controls to objects of this class.
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
<DataContract()>
Public Class Thing
Implements INotifyPropertyChanged
Private _Name As String
<DataMember()>
Public Property Name As String
Get
Return _Name
End Get
Set(value As String)
If _Name <> value Then
_Name = value
NotifyPropertyChanged("Name")
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Function SetProperty(Of T)(storage As T, value As T, <CallerMemberName> Optional PropertyName As String = "") As Boolean
If Object.Equals(storage, value) Then Return False
storage = value
NotifyPropertyChanged(PropertyName)
Return True
End Function
Public Sub NotifyPropertyChanged(PropertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
End Class
Let's add the sub class. Create a new class called Person in WCFPerson.
Public Class Person
Inherits Thing
Public Event AgeChanged(OldValue As String, NewValue As String)
Private _Age As Integer
<DataMember()>
Public Property Age As Integer
Get
Return _Age
End Get
Set(value As Integer)
If _Age <> value Then
RaiseEvent AgeChanged(_Age, value)
_Age = value
NotifyPropertyChanged("Age")
End If
End Set
End Property
Public Sub IncrementAge()
Age += 1
End Sub
End Class
This inherits Thing. Because Thing already implements INotifyPropertyChanged, the Person class does not need to explicitly. The Person class does not need DataContract because the Thing class already has it, but it still needs to mark any members that need to be part of the data contract with DataMember decorations. The Person class adds an Age property to Thing's Name property, an AgeChanged event, and an IncrementAge method.
Now it is time to define our interface and code in WCFPerson. Replace the contents of IPerson.vb with this. It defines a single method called GetPerson that returns a new Person object.
<ServiceContract()>
Public Interface IPerson
<OperationContract()>
Function GetPerson() As Person
End Interface
We will implement our interface in Person.svc.vb like so...
Public Class WCFPerson
Implements IPerson
Public Function GetPerson() As Person Implements IPerson.GetPerson
Return New Person() With {.Name = "Bob", .Age = 40}
End Function
End Class
Now we are ready to build the WCF project. Right-click on WCFPerson and select build. Once you have a clean build we will add a service reference to the ModelAcrossTheWire project by right-clicking on References and selected Add Service Reference.
Now click [Discover], set the namespace to PersonServiceReference, and click [OK].
Replace MainWindow.xaml with this. Note I'm using a poor man's version of MVVM for simplicity.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ClassAcrossTheWire"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} is {1}">
<Binding Path="Person.Name"/>
<Binding Path="Person.Age"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
Imports System.ComponentModel
Imports WCFPerson
Class MainWindow
Implements INotifyPropertyChanged
Private WithEvents _Person As Person
Public Property Person As Person
Get
Return _Person
End Get
Set(value As Person)
If Not value.Equals(_Person) Then
_Person = value
NotifyPropertyChanged("Person")
End If
End Set
End Property
Public Sub New()
InitializeComponent()
Person = GetPerson()
Person.IncrementAge()
End Sub
Public Function GetPerson() As Person
Dim svc As PersonServiceReference.PersonClient = Nothing
Try
svc = New PersonServiceReference.PersonClient()
Return svc.GetPerson()
Catch ex As Exception
Throw
Finally
If svc IsNot Nothing Then svc.Close()
End Try
End Function
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub NotifyPropertyChanged(PropertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
Private Sub AgeChanged(OldValue As String, NewValue As String) Handles _Person.AgeChanged
MessageBox.Show("Age Changed from " & OldValue & " to " & NewValue)
End Sub
End Class
So the take away from this project is that we are able to bind to the same model we used to retrieve the data. That model can include properties, methods, and events that do not interfere with the use of the model as a DataContract.
Happy birthday Bob!
Happy birthday Bob!
No comments:
Post a Comment