MVVM Overview - Cleveland WPF User Group

Download Report

Transcript MVVM Overview - Cleveland WPF User Group

MVVM Overview
Frank Shoemaker
MindCrafted Systems
[email protected]
Overview of MVVM
 Some background
 Some examples
MVVM
 MVVM stands for
 Model
 View
 View-Model
Simple Case
Model
 Typical class that covers a database
 Could be a WCF Service and its client reference
ViewModel




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()
View
 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
More on the ViewModel in MVVM
 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
Existing Stuff
Database Tables
Department
Column Name
Data Type
DepartmentID
int
Name
nvarchar(30)
StatusID
int
CreateDate
datetime
CreatedBy
varchar(255)
LastChangedDate
datetime
LastChangedBy
varchar(255)
Allow Nulls
MCStatus
MCStatusID
Name
DisplayOrder
Active
StatusID
CreateDate
CreatedBy
LastChangedDate
LastChangedBy
Item
ItemId
Name
DepartmentID
PreferredSupplierID
SpecialOrder
UnitsInStock
ReorderLevel
LastOrderDate
AveragePrice
StatusID
CreateDate
CreatedBy
LastChangedDate
LastChangedBy
Existing Stuff
Library Class
WPF
Model Class
 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 – I/O
WPF - Synchronous
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/O
Silverlight - Asynch
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/O
Silverlight - 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
ViewModel Properties
Properties come in two flavors
 Data Properties
 Name, StatusID, CreateDate, AllSelect
 Form State properties
 IsEditing, IsNotEditing,
IsOKToBeginEdit
ViewModel Methods
Methods come in two flavors
 CRUD Methods
 Create, Read, Update, Delete
 Implements the IEditableObject
interface
 BeginEdit, CancelEdit, EndEdit
ViewModel Class Events
 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 Properties
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.
Gluing the Pieces Together
 DataContext
 Binding
Setup the DataContext
 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.
Set up 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
Data Binding - ViewModel
INotifyPropertyChanged
Public Event PropertyChanged(ByVal sender As Object,
ByVal e As PropertyChangedEventArgs) _
Implements INotifyPropertyChanged.PropertyChanged
Data Binding - ViewModel
Deparment.CreatedBy
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 Set
End Property
Data Binding - View
Deparment.CreatedBy
<TextBox Text="{Binding Path=CreateDate,
Mode=OneWay}" . . .
 Path is within the DataContext
 Mode=OneWay means the control won’t update the
ViewModel
 Since it’s bound to CreateDate, when the
PropertyChanged event is raised the control reloads
from the CreateDate property in the ViewModel
 Controls don’t need to be named
Binding For State
<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"
. . .
TwoWay Data Bindning
<TextBox Text="{Binding Path=Name,
Mode=TwoWay, . . .
 Bi-directional binding.
Validating in the ViewModel
<TextBox Text="{Binding Path=Name,
Mode=TwoWay,
ValidatesOnExceptions=True}"
. . .
When the ViewModel throws the exception, the
View changes
Setting up Styles for
Validation Binding
<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>
When to Validate?
<TextBox Text="{Binding Path=Name,
Mode=TwoWay,
UpdateSourceTrigger=LostFocus,
ValidatesOnExceptions=True}"
IsEnabled="{Binding Path=IsEditing}"
. . .
ComboBox Binding
<ComboBox ItemsSource="{Binding Path=StatusSelect}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=StatusID,
Mode=TwoWay}"
. . .
Data Binding
ViewModel.FormStateMessage
 The state of Editing is maintained by the ViewModel
 ViewModel exposes properties to indicate state
 View interprets the ViewModel’s state to the user
Displaying the Status Message
<TextBlock Text="{Binding Path=FormStateMessage}"
. . .
Binding to Change Color of the
Message if it’s an Error
<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
Converter Routine
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
Setup the Converter Routine as a
Resource in the XAML
<Page.Resources>
<converter:MessageForegroundColor
x:Key="MessageForegroundColor" />
</Page.Resources>
Testing
 Since ViewModels know nothing about the UI, they
can be driven with a programmatic test case.
MVVM Wrap up
 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.