Mar 26, 2015
MVVM OverviewFrank ShoemakerMindCrafted Systems
Some background Some examples
Overview of MVVM
MVVM stands for
Model
View
View-Model
MVVM
Simple Case
Typical class that covers a database Could be a WCF Service and its client
reference
Model
Provides data to and from the View Responds to both the View and the Model Informs the View of changes in the data Reusable (at least much more than code behind
a form)
ViewModel
Knows nothing about the View Does not “push” data into the view
TextBox1.Text = object.Name()
ViewModel
Uses Binding to “subscribe” to the ViewModel
Interprets business data and state of ViewModel to the human
Nothing but Presentation - XAML No or minimal code-behind
View
WPF/Silverlight data binding is what makes things work
ViewModel presents a “published interface” to the View
Binding in the XAML instead of code More use of Declarative Programming
More on the ViewModel in MVVM
Existing StuffDatabase Tables Department
DepartmentID int
Name nvarchar(30)
StatusID int
CreateDate datetime
CreatedBy varchar(255)
LastChangedDate datetime
LastChangedBy varchar(255)
Column Name Data Type Allow Nulls
MCStatusMCStatusID
Name
DisplayOrder
Active
StatusID
CreateDate
CreatedBy
LastChangedDate
LastChangedBy
ItemItemId
Name
DepartmentID
PreferredSupplierID
SpecialOrder
UnitsInStock
ReorderLevel
LastOrderDate
AveragePrice
StatusID
CreateDate
CreatedBy
LastChangedDate
LastChangedBy
Existing StuffLibrary Class
WPF
Business data Properties
Properties to return “Select” lists (AllSelect and StatusSelect)
The usual CRUD routines
Model Class
Encapsulates how it communications with the back-end
Uses Events to signal I/O successfully completed or an error occurred
In WPF it’s synchronous, but can be used as if it’s a asynchronous.
In Silverlight it’s async.
Model Class
Public Sub Read(ByVal id As Integer)
Try myLibraryObj.MCFetch(id) RaiseEvent IOSuccessful("Read", New EventArgs())
Catch ex As Exception RaiseEvent IOErrorOccurred("Read", ex)
End Try
End Sub
Model Class – I/OWPF - Synchronous
Public Sub Read(ByVal id As Integer)
Try myService.FetchAsync(id,
ServiceReference1.myContextPayloadType.Heavy)
Catch ex As Exception RaiseEvent IOErrorOccurred("Read", ex)
End Try
End Sub
Model Class – I/OSilverlight - Asynch
Private Sub Read_Completed(ByVal sender As System.Object, ByVal e As
ServiceReference1.FetchCompletedEventArgs) Handles myService.FetchCompleted
If IsNothing(e.Error) Then myData = e.Result RaiseEvent IOSuccessful("Read", New EventArgs()) Else RaiseEvent IOErrorOccurred("Read", e.Error) End If
End Sub
Model Class – I/OSilverlight - Asynch
Properties come in two flavorsData Properties
Name, StatusID, CreateDate, AllSelect
Form State properties IsEditing, IsNotEditing,
IsOKToBeginEdit
ViewModel Properties
Methods come in two flavorsCRUD Methods
Create, Read, Update, Delete
Implements the IEditableObject interface
BeginEdit, CancelEdit, EndEdit
ViewModel Methods
Implements INotifyPropertyChanged interface
Notify the View that a property has changed its value
Allows the View to respond to reflect the change, if it wants to
ViewModel Class Events
Present some properties in more than one way for the convenience of the View
IsEditing True if the user is currently editing
the business object
IsNotEditing True if the user is NOT currently
editing the business object.
ViewModel Class Properties
DataContext
Binding
Gluing the Pieces Together
This example sets up the DataContext in code Create a new instance of the ViewModel Bind the View to the ViewModel Instance
All Controls on the View then “inherit” the DataContext from the page.
Setup the DataContext
Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
myVM = New ViewModel.DepartmentVM()
' Set the DataContext for the ' entire page to the ViewModel Me.DataContext = myVM
End Sub
Set up the DataContext
Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) _ Implements INotifyPropertyChanged.PropertyChanged
Data Binding - ViewModelINotifyPropertyChanged
Public Property CreatedBy() As String Get Return myModel.CreatedBy End Get Set(ByVal value As String) myModel.CreatedBy = value RaiseEvent PropertyChanged(Me, New
PropertyChangedEventArgs("CreatedBy")) End SetEnd Property
Data Binding - ViewModelDeparment.CreatedBy
<TextBox Text="{Binding Path=CreateDate, Mode=OneWay}" . . .
Path is within the DataContextMode=OneWay means the control won’t update the ViewModelSince it’s bound to CreateDate, when the PropertyChanged event is raised the control reloads from the CreateDate property in the ViewModelControls don’t need to be named
Data Binding - ViewDeparment.CreatedBy
<TextBox Text="{Binding Path=Name,Mode=TwoWay, IsEnabled="{Binding
Path=IsEditing}" . . .
Binding to interpret the ViewModel’s state to the user.
Binding For State
<Button Name="Edit" Content="Edit" IsEnabled="{Binding Path=IsOKToBeginEdit}" Click="Edit_Begin" . . .
<Button Name="Save" Content="Save" IsEnabled="{Binding Path=IsEditing}" Click="Edit_Save" . . .
<Button Name="Cancel" Content="Cancel" IsEnabled="{Binding Path=IsEditing}" Click="Edit_Cancel" . . .
Binding for State
<TextBox Text="{Binding Path=Name,Mode=TwoWay, . . .
Bi-directional binding.
TwoWay Data Bindning
<TextBox Text="{Binding Path=Name,Mode=TwoWay, ValidatesOnExceptions=True}". . .
When the ViewModel throws the exception, the View changes
Validating in the ViewModel
<Style x:Key="styleTextBox" TargetType="TextBox"> . . . <Setter Property="Validation.ErrorTemplate" Value="{StaticResource errorEyeCatcher}"/> <Style.Triggers> <Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/> </Trigger> </Style.Triggers></Style>
Setting up Styles for Validation Binding
<!--Display a Red * next to the control with an error --><ControlTemplate x:Key="errorEyeCatcher"> <DockPanel> <Border BorderBrush="Red" BorderThickness="1" Padding="2"> <AdornedElementPlaceholder/> </Border> <TextBlock Foreground="Red" FontSize="24" Text="*"/> </DockPanel></ControlTemplate>
Setting up Styles for Validation Binding
<TextBox Text="{Binding Path=Name,Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True}"IsEnabled="{Binding Path=IsEditing}"
. . .
When to Validate?
<ComboBox ItemsSource="{Binding Path=StatusSelect}"
DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding
Path=StatusID, Mode=TwoWay}"
. . .
ComboBox Binding
The state of Editing is maintained by the ViewModel
ViewModel exposes properties to indicate state
View interprets the ViewModel’s state to the user
Data BindingViewModel.FormStateMessage
<TextBlock Text="{Binding Path=FormStateMessage}" . . .
Displaying the Status Message
<TextBlock Text="{Binding Path=FormStateMessage}" Foreground="{Binding Path=FormStateMessageType, Converter={StaticResource MessageForegroundColor}, ConverterParameter=FormStateMessageType}"
. . .
Use a converter routine to transform integer from the ViewModel into a color on theTextBox
Binding to Change Color of the Message if it’s an Error
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type,
ByVal parameter As Object, ByVal culture As
System.Globalization.CultureInfo) _ As Object Implements
System.Windows.Data.IValueConverter.Convert
If CInt(value) = 1 Then ' Informational message Return "Black" Else ' Error message Return "Red" End If
End Function
Converter Routine
<Page.Resources> <converter:MessageForegroundColor x:Key="MessageForegroundColor" /></Page.Resources>
Setup the Converter Routine as a Resource in the XAML
Since ViewModels know nothing about the UI, they can be driven with a programmatic test case.
Testing
Loose coupling between the Model, ViewModel, and View
Bright lines between areas of concerns
At least some chance of reusability of the ViewModel
ViewModel is independently testable
MVVM Wrap up
View can be worked on by designers without messing up the ViewModel
Would benefit from a root ViewModel class for the state management.
MVVM Wrap up