Model-View-ViewModel (MVVM) is a proven design pattern used heavily in many WPF / Silverlight applications. This article focuses on implementing MVVM on existing applications rather than starting a new application. In this article we are going to see how we can take small steps towards MVVM.
1. Introduction
Writing an application with good practices and proven
architecture design is comparatively easy if we are going to write a new
application. But most of the time we are working on existing applications,
where it is not easy to make changes. If we ever get the chance to rewrite the
existing application, we probably won't repeat the same mistakes that we or
someone else made due to time constraints, technology limitation, scope creep
etc. It would be better if we could refactor the existing codebase for
betterment at the same time, with minimal or no risk effect.
Model-View-ViewModel
(MVVM) is a proven design pattern used heavily in many WPF / Silverlight applications. But it might
be possible that we already have a lot of code base that is not taking an
advantage of it. This article focuses on implementing MVVM on existing applications
rather than starting a new application. In this article we are going to see how
we can take small steps towards MVVM. In the first part of this article, we are
going to study the evaluation of MVVM and how we came to this point. The second
part will examine the different steps to achieve MVVM. Although some steps have
a major rework with the potential of high risk, there are still few things that
have little or no effect on the project.
2. Evaluation of MVVM
Let's look at MVVM from a higher level and take a step-by-step
approach to understanding it. Here our discussion is based on the complexity of
the architecture from simple to complex, not from an historical order.
Probably the simplest design principle to separate the data
from its presentation is Observer design pattern [1]. In Observer design
pattern, we have two different classes for data (subject/model) or its
presentation (observer/view). Subject classes contain the instance of all of
the observers and send notification to all of observers when there is any
change in the data. Here is a simple block diagram of observer design pattern.
Figure 1: Observer Desian Pattern
The next step is to introduce the middle layer in between
the data and its presentation. The main purpose of this layer is to communicate
between these two components. This is a main concept of Model View
Controller (MVC) [2]. It is shown by this block diagram.
Figure 2: MVC
This approach has some advantages and disadvantages. The
main disadvantage is that our view is not totally independent of our model. Model View
Presenter (MVP) [3] handles exactly the same problem. In the MVP model there
is no relation between View and Model.
Figure 3: MVP
MVVM is very similar to MVP pattern. Or it is some sort of
specialized form of MVP pattern. In MVVM, Presentator is known as ViewModel.
Model communicates with ViewModel with notification and ViewModel communicates
with View with data binding and command binding as shown by this block diagram.
Figure 4: MVVM
Now let's take a more detailed look at MVVM. What is the
biggest advantage of this? Its first advantage is that our presentation is
totally unaware of our model. We don't write any user interface specific code
in ViewModel and all the communication is based on data binding and command
binding, which means we can easily write a unit tests for it. We can easily
change any user interface or even change the data model easily. Here is a
detail block diagram of MVVM.
Figure 5: MVVM
This diagram explains how we can take advantage of MVVM. The
most important thing in this pattern is to properly design the ViewModel. WPF
has very orthogonal design. It means we can customize or enhanced different
parts of the library without affecting others. Most of the WPF reusability is
based on composition rather than inheritance; therefore we can take maximum
advantage of it, even at run time, because we can easily change the composition
behavior at run time but not the inherited components. In this block diagram we
see the major components of the WPF class library that we developed, enhanced
or customized in most of the WPF application.
Figure 6: ViewModel
3. Switching to MVVM
We are going to start with a simple application, that is not
written in WPF and gradually make step-by-step changes in it to introduce MVVM
pattern. Our starting point is a loan
amortization application written in VC++ and using WPF. The reason to pick
that application is that by definition we can't use MVVM with VC++; XAML has very
limited support in VC++ (The only support as of now is to load XAML at runtime
and use it).
It is possible that we have some properties in our class
that store data, but we usually have fields to store information. In our
starting project, we have a class to store information about each payment. Here
is our class.
public class PaymentInfo
{
public int PaymentNo
{ get; set; }
public double Payment
{ get; set; }
public double Principle
{ get; set; }
public double Interest
{ get; set; }
public double Balance
{ get; set; }
}
We have field variables to store information from the user
interface and display it back to the user interface.
private double principle;
private double interestRate;
private int duration;
private double payment;
Here is a piece of code to get user input and store it in
field variables
if (txtPrincipleAmount.Text.Length > 0)
{
principle = Convert.ToDouble(txtPrincipleAmount.Text);
}
else
{
MessageBox.Show("Please enter principle amount", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
if (txtInterestRate.Text.Length > 0)
{
interestRate = Convert.ToDouble(txtInterestRate.Text);
interestRate /= 100;
interestRate /= 12;
}
else
{
MessageBox.Show("Please enter interest", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
if (txtDuration.Text.Length > 0)
{
duration = Convert.ToInt32(txtDuration.Text);
}
else
{
MessageBox.Show("Please enter duration", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
We have some utility methods to perform our calculations,
which we call from our event handler.
// Calculate the remaining balance at particular payment
private double CalculateBalance(int month)
{
double interestTerm = Math.Pow((1 + interestRate), month);
double totalInterest = principle * interestTerm;
double totalPaid = payment * (interestTerm - 1) / interestRate;
return totalInterest - totalPaid;
}
// Calculate the Interest part of any particular payment
private double CalculateInterestPart(int month)
{
double interestTerm = Math.Pow((1 + interestRate), (month - 1));
double totalInterest = principle * interestTerm;
double totalPaid = payment * (interestTerm - 1) / interestRate;
return (totalInterest - totalPaid) * interestRate;
}
// Calculate the principle part of any particular payment
private double CalculatePrinciple(int month)
{
return payment - CalculateInterestPart(month);
We have one method to calculate the amortization schedule
for the complete loan period and add those values in DataGrid.
// Calculate the complete amortization schedule and fill the data grid control
private void CalculatePayment()
{
int totalpayments = duration * 12;
Title = "Amortization Schedule for " +
Convert.ToString(totalpayments) + " Payments";
// calculate interest term
double interestTerm = Math.Pow((1 + interestRate), totalpayments);
// calculate payment
payment = (principle * interestRate) / (1 - (1 / interestTerm));
payments.Clear();
for (int iIndex = 1; iIndex <= totalpayments; ++iIndex)
{
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.PaymentNo = iIndex;
paymentInfo.Balance = CalculateBalance(iIndex);
paymentInfo.Payment = payment;
paymentInfo.Interest = CalculateInterestPart(iIndex);
paymentInfo.Principle = CalculatePrinciple(iIndex);
payments.Add(paymentInfo);
}
lstAmortization.ItemsSource = payments;
}
Note that the user interface code is heavily coupled inside
the business logic, in this case calculating the loan amortization. There is no
simple way to write test cases to check this calculation without involving the user
interface.
The XAML of our project is very simple. Here is the complete
XAML code of our program.
<Window x:Class="MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="400" Width="600">
<Grid Background="Beige">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" FontSize="18" FontWeight="Bold"
HorizontalAlignment="Center" VerticalAlignment="Center">Loan Amortization</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" Margin="5">Principle Amount</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="2" VerticalAlignment="Center" Margin="5">Interest Rate</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="3" VerticalAlignment="Center" Margin="5">Duration</TextBlock>
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" VerticalAlignment="Center" Name="txtPrincipleAmount"/>
<TextBox Grid.Column="1" Grid.Row="2" Margin="5" VerticalAlignment="Center" Name="txtInterestRate"/>
<TextBox Grid.Column="1" Grid.Row="3" Margin="5" VerticalAlignment="Center" Name="txtDuration"/>
</Grid>
<DataGrid Grid.Row="1" Name="lstAmortization" Margin="5">
</DataGrid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Name="btnCalculate" Width="75" Height="45" Click="btnCalculate_Click">Calculate</Button>
<Button Grid.Column="1" Name="btnExit" Width="75" Height="45" Click="btnExit_Click">Exit</Button>
</Grid>
</Grid>
</Window>
Also note that like other programming styles, here we have the
name of every control.
3.1. Step 1: Use Properties
If code is using a field not properties, then convert those
from field to properties. (This should have no or minimal effect on existing
source code, because I chose property names very similar to (or the same) as
fields. We simply change the uses the properties instead of fields in this step.
Here is an updated version of our program.
public double Principle
{ get; set; }
public double InterestRate
{ get; set; }
public int Duration
{ get; set; }
public double Payment
{ get; set; }
There is no change in XAML of the program. If we use the same
property name as the previously used field, then we don't have to change
anything in the code either. In our example, I used the letter case for
properties and camel case for fields, so we updated the source code accordingly
and use letter case there.
3.2. Step 2: Implement INotifyPropertyChanged Interface / Dependency Property
My first step is to change the data model to make it WPF
friendly. To do this I will either implement the INotifyPropertyChange
interface or make them dependency property. If we already have properties, then
this should have no effect at all for the rest of the project. The rest of the
code should be the same and should work the same, but at least now you have
your model ready.
In this example we are going to change our properties to
dependency properties. In the coming steps we will see the example of the
INotifyPropertyChanged interface too, to see both variants. Here is our updated
code.
public static readonly DependencyProperty PrincipleProperty =
DependencyProperty.Register("Principle", typeof(double), typeof(MainWindow));
public double Principle
{
get { return (double)GetValue(PrincipleProperty); }
set { SetValue(PrincipleProperty, value); }
}
public static readonly DependencyProperty InterestRateProperty =
DependencyProperty.Register("InterestRate", typeof(double), typeof(MainWindow));
public double InterestRate
{
get { return (double)GetValue(InterestRateProperty); }
set { SetValue(InterestRateProperty, value); }
}
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(int), typeof(MainWindow));
public int Duration
{
get { return (int)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
public static readonly DependencyProperty PaymentProperty =
DependencyProperty.Register("Payment", typeof(double), typeof(MainWindow));
public double Payment
{
get { return (double)GetValue(PaymentProperty); }
set { SetValue(PaymentProperty, value); }
}
There is no change in the XAML file.
3.3. Step 3: Use Data Binding
The next step is using Data Binding. This will reduce lots
of thecodebase. Now it shouldn't have any code like this:
txtName.Text = firstName;
This is a major change in the code base. But at this step I focus
only on data, not on behavior. My code still has event handlers and should work
fine, but now I don't have to worry about updating the data into control or
getting data from control to my variable. (No more control to variable
interaction). Now we only check the invalid input directly from the properties
(they automatically get values from the user interface). For validation purposes,
we are going to introduce a new class inherited by ValidationRule class. Here
is the code of our new class.
public class MyValidation : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
int number = Int32.Parse((string)value);
if (number < 0)
{
return new ValidationResult(false, "Please enter values greater than zero.");
}
return new ValidationResult(true, null);
}
}
Now we have to update our XAML and use the Data binding
there. Here is an updated version of our XAML. In this version we also include
our validation class to perform validation.
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" VerticalAlignment="Center">
<TextBox.Text>
<Binding Path="Principle">
<Binding.ValidationRules>
<local:MyValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Grid.Column="1" Grid.Row="2" Margin="5" VerticalAlignment="Center">
<TextBox.Text>
<Binding Path="InterestRate">
<Binding.ValidationRules>
<local:MyValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Grid.Column="1" Grid.Row="3" Margin="5" VerticalAlignment="Center">
<TextBox.Text>
<Binding Path="Duration">
<Binding.ValidationRules>
<local:MyValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
We also define one style to display the error message when
validation fails. Here is the code of our style.
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Also note that now we no longer need to specify the name of
control; with the help of data binding we will automatically get the values in
the correct property (dependency property in this example).
3.4. Step 4: Refactor Event Handler
This is an intermediate step. Now I already have all the
data handling in the form of data binding with no control/variable code in it.
I am going to iterate through all event handlers and whatever code is written
there, move them into a method and call that method from the event handler.
This is the updated version of our event handler now.
private void btnCalculate_Click(object sender, RoutedEventArgs e)
{
CalculatePayment();
}
3.5. Step 5: Implement ICommand Interface
In the next step I am going to introduce the ICommand interface
and implement it. There is no change for the existing codebase. Here is one
example of it.
public class MyCommand : ICommand
{
public Action Function
{ get; set; }
public MyCommand()
{
}
public MyCommand(Action function)
{
Function = function;
}
public bool CanExecute(object parameter)
{
if (Function != null)
{
return true;
}
return false;
}
public void Execute(object parameter)
{
if (Function != null)
{
Function();
}
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
There is no change in XAML of our project.
3.6. Step 6: Add ICommand Properties
Now I am going to add properties in my class (which has
event handler earlier). The type of that property is ICommand. This again
should not have any effect on the rest of the code. The number of properties
should be at least the same as the method I called inside the event handler and
I chose a similar name.
public ICommand ExitCommand
{ get; set; }
public ICommand CalculateAmortizationCommand
{ get; set; }
There is no change in XAML of our project.
3.7. Step 7: Assign Methods to ICommand Type Properties
I have already created the method to calculate amortization
(or do other work). Create object of MyCommand class and assign it to our
ICommand properties in constructor.
ExitCommand = new MyCommand(Close);
CalculateAmortizationCommand = new MyCommand(CalculatePayment);
There is no change in XAML of our project.
3.8. Step 8: Use Command Binding
Remove the event handler from the code as well as from XAML
and instead use the command binding. This is again a major change in the code
(both code and XAML).
<Button Grid.Column="0" Name="btnCalculate" Width="75" Height="45" Command="{Binding CalculateAmortizationCommand}" >Calculate</Button>
<Button Grid.Column="1" Name="btnExit" Width="75" Height="45" Command="{Binding ExitCommand}" >Exit</Button>
Also note that now all of our business logic is in method,
not in event handler, which means now we can somehow perform unit testing on
those methods without any user interface involvement.
3.9. Step 9: Define ViewModel Class
Define a class to ViewModel and move all ICommand interface
properties and methods assigned to it in this class. We introduced one event
type property in our ViewModel class to handle the close event.
public event EventHandler RequestClose;
private void CloseWindow()
{
EventHandler handler = this.RequestClose;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
We set this event handler from our window (view) class.
There are multiple ways to do this, but here we are doing this with unnamed
delegate.
public partial class MainWindow : Window
{
private MyViewModel vm = new MyViewModel();
public MainWindow()
{
InitializeComponent();
vm.RequestClose += delegate
{
Close();
};
DataContext = vm;
}
}
Here is the complete code of our ViewModel class.
public class MyViewModel : DependencyObject
{
public static DependencyProperty PrincipleProperty =
DependencyProperty.Register("Principle", typeof(double), typeof(MainWindow));
public double Principle
{
get { return (double)GetValue(PrincipleProperty); }
set { SetValue(PrincipleProperty, value); }
}
public static DependencyProperty InterestRateProperty =
DependencyProperty.Register("InterestRate", typeof(double), typeof(MainWindow));
public double InterestRate
{
get { return (double)GetValue(InterestRateProperty); }
set { SetValue(InterestRateProperty, value); }
}
public static DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(int), typeof(MainWindow));
public int Duration
{
get { return (int)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
public static DependencyProperty PaymentProperty =
DependencyProperty.Register("Payment", typeof(double), typeof(MainWindow));
public double Payment
{
get { return (double)GetValue(PaymentProperty); }
set { SetValue(PaymentProperty, value); }
}
public event EventHandler RequestClose;
public ObservableCollection Payments
{ get; set; }
public ICommand CalculateAmortizationCommand
{ get; set; }
public ICommand ExitCommand
{ get; set; }
public MyViewModel()
{
CalculateAmortizationCommand = new MyCommand(CalculatePayment);
ExitCommand = new MyCommand(CloseWindow);
Payments = new ObservableCollection();
}
private void CloseWindow()
{
EventHandler handler = this.RequestClose;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
// Calculate the complete amortization schedule and fill the list control
private void CalculatePayment()
{
int totalpayments = Duration * 12;
double monthlyInterest = CalculateMonthlyInterest(InterestRate);
// calculate interest term
double interestTerm = Math.Pow((1 + monthlyInterest), totalpayments);
// calculate payment
Payment = (Principle * monthlyInterest) / (1 - (1 / interestTerm));
Payments.Clear();
for (int iIndex = 1; iIndex <= totalpayments; ++iIndex)
{
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.PaymentNo = iIndex;
paymentInfo.Balance = CalculateBalance(iIndex);
paymentInfo.Payment = Payment;
paymentInfo.Interest = CalculateInterestPart(iIndex);
paymentInfo.Principle = CalculatePrinciple(iIndex);
Payments.Add(paymentInfo);
}
//lstAmortization.ItemsSource = payments;
}
// Calculate the remaining balance at particular payment
private double CalculateBalance(int month)
{
double monthlyInterest = CalculateMonthlyInterest(InterestRate);
double interestTerm = Math.Pow((1 + monthlyInterest), month);
double totalInterest = Principle * interestTerm;
double totalPaid = Payment * (interestTerm - 1) / monthlyInterest;
return totalInterest - totalPaid;
}
// Calculate the Interest part of any particular payment
private double CalculateInterestPart(int month)
{
double monthlyInterest = CalculateMonthlyInterest(InterestRate);
double interestTerm = Math.Pow((1 + monthlyInterest), (month - 1));
double totalInterest = Principle * interestTerm;
double totalPaid = Payment * (interestTerm - 1) / monthlyInterest;
return (totalInterest - totalPaid) * monthlyInterest;
}
// Calculate the principle part of any particular payment
private double CalculatePrinciple(int month)
{
return Payment - CalculateInterestPart(month);
}
// Calculate the monthly interest rate
private double CalculateMonthlyInterest(double InterestRate)
{
double monthlyInterest = InterestRate;
monthlyInterest /= 100;
monthlyInterest /= 12;
return monthlyInterest;
}
}
Note this class doesn't have any user interface element so
we can easily write test cases on it. In addition to this, if we want to change
the presentation of this, such as WPF to Silverlight or a Console based
application, we can do it very easily. This class contains all the business
logic and now it is up to the user of this class how to present that
information.
3.10. Step 10: Define ViewModelBase Class
Let's do one more step. We might want to do the same thing
again and again, so why not create one base class to have minimum ViewModel
functionality to reuse it in other ViewMode classes. One more reason to create a
ViewModelBase class is to show how to implement the INotifyPropertyChanged
interface. Here is the code of our base class.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event EventHandler RequestClose;
public ICommand ExitCommand
{ get; set; }
public void Close()
{
EventHandler handler = this.RequestClose;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Also note that we made our ViewModelBase class abstract to
avoid creating an object of this class. Here is part of our ViewModel class
inherited by ViewModelBase.
public class MyViewModel : ViewModelBase
{
public double principle;
public double interestRate;
public int duration;
public double payment;
public double Principle
{
get { return principle; }
set
{
principle = value;
RaisePropertyChanged("Principle");
}
}
public double InterestRate
{
get { return interestRate; }
set
{
interestRate = value;
RaisePropertyChanged("InterestRate");
}
}
public int Duration
{
get { return duration; }
set
{
duration = value;
RaisePropertyChanged("Duration");
}
}
public double Payment
{
get { return payment; }
set
{
payment = value;
RaisePropertyChanged("Payment");
}
}
}
In this class we raise the property change event whenever
there is a change in the property. There are lots of other things we can do to
improve our code base, but this is just a first step in the right direction. We
can easily include more steps in the following guidelines, but after following
these steps we will have a reasonably good design of our program towards MVVM.
Here is the output of this program.
[MVVM_07.gif]
Figure 7: Loan Amortization Output
4. References
1. Design Pattern, Elements of Reusable Object Oriented Software
Erich Gamm, Richard Helm, Ralph Johnson, John Vlissides
2. A
Cookbook for Using the Model-View-Controller User Interface Paradigm in
Smalltalk-80
Glenn E. Krasner, Stephen T. Pope
Journal of Object Oriented Programming, August/September 1988
http://www.ics.uci.edu/~redmiles/ics227-SQ04/papers/KrasnerPope88.pdf
3, GUI
Architectures
Martin Fowler
http://www.martinfowler.com/eaaDev/uiArchs.html
4. Loan
Amortization Application in WPF using C++
Zeeshan Amjad
http://www.codeproject.com/KB/WPF/LoanAmortizationWPF.aspx
http://www.codeguru.com/cpp/cpp/cpp_managed/general/print.php/c16355/