Introduction:- Purpose of this document is to provide a basic
idea to the Model – View – ViewModel pattern. This document is prepared
considering the beginners who are learning the pattern. Here I used simple wpf
application code sample to explain the pattern.
Purpose:- The main purpose of MVVM pattern is
to decouple the UI and code base, they can interact with the “DataBinding”
concept. This concept provides more flexibility to change our user interface
without any change in code base. Any change in data is communicated with the
help of notifications.
The Pattern:-The core components of MVVM pattern
are: Model, View and ViewModel. The following image will explain relationship
between these three components.
Let me explain the each component
with simple code. Let us have an example of showing the employee data available
in the xml file as shown below:-
<Employees>
<Employee>
<Name>Leesa J</Name>
<Phone>987654321</Phone>
<Email>neils@company.com</Email>
<Address>Neils Apt, 1st Block,3rd Cross, ABC street, XYZ State,Pin:123456 </Address>
</Employee>
</Employees>
For
simplification purpose I have created one employee data, later you can
introduce some more that according to your requirements. Let me explain the
three components of MVVM.
1.The
Model:- The model is the representation of data, in our case it is class that
represents a employee. Let is create a model object called employee as shown
below:-
public class Employee
{
private string myname;
private int myPhoneNo;
private string myEmail;
private string myAddress;
public Employee()
{
XmlDocument myDocument = new XmlDocument();
myDocument.Load("EmployeeInfo.xml");
myname = myDocument.SelectSingleNode(@"//Employees/Employee/Name" ).FirstChild.Value;
myPhoneNo = Convert.ToInt32(myDocument.SelectSingleNode("//Employ ees/Employee/Phone").FirstChild.Value);
myEmail = myDocument.SelectSingleNode("//Employees/Employee/Email").FirstChild.Value;
myAddress = myDocument.SelectSingleNode("//Employees/Employee/Address").FirstChild.Value;
}
public string Name
{
get { return myname; }
set { myname = value; }
}
public int PhoneNo
{
get { return myPhoneNo; }
set { myPhoneNo = value; }
}
public string Email
{
get { return myEmail; }
set { myEmail = value; }
}
public string Address
{
get { return myAddress; }
set { myAddress = value; }
}
}
As I said
earlier this class is data representation, so for simplicity we read the xml
file and populate the fields in the constructor itself.
2. ViewModel
:- This is a bridge between our view and model. It acts like a business logic. this exposes
methods, commands and other points that help maintain the state of view,
manipulates model as result of action. In our application we will have a simple class that
holds the object of our model.
public class EmployeeViewModel
{
private Employee myData;
public EmployeeViewModel()
{
myData = new Employee();
}
public Employee EmployeeData
{
get { return myData; }
set { myData = value; }
}
}
3. View :-
This is actual data presenter. the view and view model communicate via data
binding. ViewModel binding to view can be done by setting the DataContext of
the view. Let us create a WPF form (View) that is bound to ViewModel as shown
below
<Window x:Class="SimpleMVVMExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="284" Width="525"
xmlns:local="clr-namespace:SimpleMVVMExample">
<Window.DataContext>
<local:EmployeeViewModel x:Name="MyViewModel" />
</Window.DataContext>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="Name" />
<TextBox HorizontalAlignment="Left" Height="23" Text=" {Binding Path=EmployeeData.Name}" Width="120" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Phone" HorizontalAlignment="Left" />
<TextBox Height="23" Text=" {Binding Path=EmployeeData.PhoneNo}" Width="120" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Email" />
<TextBox Text="{Binding Path=EmployeeData.Email}"
VerticalAlignment="Top" Width="120" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Address" Width="46" />
<TextBox TextWrapping="Wrap" Text=" {Binding Path=EmployeeData.Address}"
VerticalAlignment="Top" Width="120" />
</StackPanel>
</StackPanel>
Run the
application you could see the data available in xml file appear on text boxes of form
without having any code behind in your form. Now put a brake point in employee
class’s Name property and change the Name text, you could see break point hits
there this is the beauty of the MVVM J
Here we are
populating the data in employee object while constructing the form object
itself. Now let us suppose what happens if we update the data dynamically (Once
form is created and loaded). Comment the code inside constructor of EmployeeViewModel, run the application and check you could observe no data
populated.
Notification:
Now let us add a button in my form
we will use click event to load the employee data (Note: Here we used click
event of button for explanation, for complete decoupling we need to use command binding to button)
<StackPanel Orientation="Horizontal">
<Button Content="LoadData" Margin="200,0,0,0" Click="Button_Click" />
</StackPanel>
Add a public
method in EmployeeViewModel
to load the data.
public void LoadData()
{
EmployeeData = new Employee();
}
Call this
method in button click event.
private void Button_Click(object sender, RoutedEventArgs e)
{
MyViewModel.LoadData();
}
Run the
application and click on LoadData button. Still you will not able to see the
data populated on the UI but if you put the breakpoint and check the execution
everything looks perfect.
So what went
wrong???
public class EmployeeViewModel:INotifyPropertyChanged
{
private Employee myData;
public EmployeeViewModel()
{
//myData = new Employee();
}
public void LoadData()
{
EmployeeData = new Employee();
}
public Employee EmployeeData
{
get { return myData; }
set
{
myData = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("EmployeeData"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
While raising the propertychanged event we pass the name of the property that is been changed in PropertyChangedEventArgs object. so that ui will come to know which property is been changed.
Create Commands:
As I already said that to achieve the complete decoupling of UI we use commands instead of events. Advantage of commands is that we can bind the same command for multiple items intead of creating events of each items (example: Cut , Copy and Paste can be achieved with button as well as context menus).
Now in our previous example we used "Buuton_Click" event to load the employee data. Let us do it with the help of command. As first step we will create a class that inherits "ICommand" interface with name "LoadCommand" as shown below
public class LoadCommand:ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
EmployeeViewModel model = parameter as EmployeeViewModel;
model.LoadData();
}
}
This class implements two interface methods CanExecute and Execute with a parameter. here I have type casted the parameter object to "EmployeeViewModel" as I will be sending that object as parameter object in XAML. In order to bind the command to UI I need to have a property of the command in my view model. Let us create a property of the command in viewmodel and instantiate it in the view model constructor.
private LoadCommand myCommand;
public EmployeeViewModel()
{
myCommand = new LoadCommand();
}
Now let us bind the command to our button as shown bellow
<StackPanel Orientation="Horizontal">
<Button Content="LoadData" Margin="200,0,0,0" Command="{Binding Path=CommandLoad}" CommandParameter="{Binding}"/>
</StackPanel>
Here you could see I have used binding as my "CommandParameter" this says that the object I have bounded i.e "EmployeeViewModel" Is used as the parameter.
Now remove the event code used in the forms code behind and run the application and check the output whether it is working as you expected. You are clear that on click of button Execute method is been called. Ok, then what is the use of CanExecute? Just return false instead of true and check the button is disabled there.This is the beauty of it without any much code we could enable and disable the button. We for simplicity i didn't used any logic here. You could use the logic as per your requirement by using the parameter object.

No comments:
Post a Comment