How does MVVM-Model-View-ViewModel work in WPF

+1 vote
asked Jul 11, 2013 in Software Practices by anonymous

I have read some articles about MVVM design pattern but not having complete knowledge on that and not sure how the communication is happening between ViewModel and View. I know that we need to implement INotifyPropertyChanged. But how the View is really able to get the updates from the ViewModel? If I have ViewModel separately then what should I have in Model? Please help me understand this concept. Thanks in advance.

Share

2 Answers

+1 vote
answered Jul 11, 2013 by Aadhira (1,213 points)
edited Jul 11, 2013 by Aadhira
 
Best answer

MVVM design pattern is specifically for WPF or Silverlight applications because of the built in support provided by the dependency objects. If you are looking for similar design pattern for winforms, then take a look MVP-VM design pattern which is similar to MVVM but suitable for winforms application.

MVVM, MVP, MVC, MVP-VP all of these design patterns are basically designed to implement separation of concerns. That means, separate the functionalities into multiple layers and each layer will have its own responsibility. In MVVM design pattern M, V, VM each has its own responsibility.

M – Model: Model has different meaning based on the context. Model can be a domain model or a model which is used to communicate to database. We will consider the model as a business entity. All business logics will go into the model. (You can have more segregated layers in the Model)
V- View: View is simply WPF screen and its code behind. Its responsibility is to get the user inputs or show information from the repository. No other complicated logic will be written in view.
VM-View Model: This one is a new layer came with the MVVM design pattern. View model is a model (of course different from Domain Model, in our case business entity) which is used to get data from Model object and do manipulation on the data in such a way the View will be expecting. In most of the scenario, we cannot retrieve the data from Model object and directly display in the View .So we will implement that kind of manipulation in view model and ViewModel should only be used in View layer.

General Rule in MVVM:
In this design pattern, the communication between the M,V,VM is very important to understand.

View may or may not have reference about ViewModel.
Same like that ViewModel may or may not have reference of View. Sometime, both may not have reference to each as direct reference. But they may be using some interface.
But, at any situation, View should never have reference of Model. Also, Model should not have reference of View. That means, both must not know about each other.
Both View and Model will be communicating between each other through ViewModel only. Below image will help you understand in clear.

Model View ViewModel Design Pattern for WPF

I really want keep the example very simple. So, we have “Register” screen with FirstName, LastName of the customer. When we click Submit button, that calls data access layer to save the customer. Second screen is “CustomerDetail” that displays Full name (FirstName + LastName) of the customer from repository.

Update changed from ViewModel to View:
In WPF, View and View model will be communicating between each other using events. Whenever there is a change in the property value of the ViewModel, it will raise an event to inform the Control in WPF window (ex. Textbox’s text property), that the particular property has been changed. Then the control will display the changed value.

When I say WPF controls listen to an event it will be listening to a very particular event called “PropertyChanged” event. But how? WPF is full of Dependency Object. Most of the properties of WPF controls are Dependency object. Ex. Text property of the Textbox. Dependency object provides many built in functionalities. One of the features is to automatically listen to “PropertyChanged” event from the property which is Bind with the control’s property. To implement this Property Changed event, .Net framework has given one interface called “INotifyPropertyChanged” interface. This Interface has abstraction of “PropertyChanged” event. So, if you want your ViewModel to update the changes to the View, then ViewModel must implement “INotifyPropertyChanged” event.

You may have a question that, if we need to bind FirstName and LastName property, where these 2 properties to be defined? Whether we want to have these properties in ViewModel? Or Model or in Both? Because the same 2 properties may need to be in Model also, if our business logic is using FirstName and LastName. Because Model is what going to be used in all other layers. If that is the case, then we will have duplicate copy, because same will be in ViewModel as well as Model.

How do solve this? Well, we will have these 2 properties in Model and have a property in ViewModel which is of type Model. In that case, Both ViewModel and Model at the minimum, must implement “INotifyPropertyChanged” interface. So, our Textboxes will be bound with the property in ViewModel , which internally uses the property in the Model. So, whenever the value of the property in Model got changed, will be directly notified to View.

Update changes from View To ViewModel:
Same like this, whenever there is a change in WPF Controls and if you want to update that value to the Property in ViewModel, then we need to use “UpdateSourceTrigger” property which is in the “Binding “ class. Without using this property, if the user enter value in the textbox, that will not be reflected in to the property in the Model through ViewModel.

Button Click Event:
One other thing to know is, WPF has the concept called Routed Events and Command. Commands are used to do some action when the button clicked. We will a separate class to implement another interface called “ICommand”. Once we implement, that particular class type will used as a property in the ViewModel. The constructor of the Command class will be accepting an “Action” which is going to be the method in ViewModel. Then Command Property will be Bind with the Button Command in View.

Since the answer box does not accept this big answer, I have posted the sample code as second answers.

+1 vote
answered Jul 11, 2013 by Aadhira (1,213 points)
edited Jul 19, 2013 by Aadhira

I hope we have the basic understanding of how, the MVVM works. Let’s jump into code sample.
I don’t think I need to explain the much in this example. It will be very easy to understand if you have read the previous answer.

Model:

public class CustomerDetail : INotifyPropertyChanged
{
 private string firstName;
 public string FirstName
 {
  get{ return firstName; }
  set{ firstName = value;
       OnPropertyChanged("FirstName");}
 }
 private string lastName;
 public string LastName
 {
 get{ return lastName;}
 set{ lastName = value;
      OnPropertyChanged("LastName");}
 }
 public event PropertyChangedEventHandler PropertyChanged;
 private void OnPropertyChanged(string propertyName)
 {
  if (PropertyChanged != null)
  {
   PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
 }
}

In this View, we have used UpdateSourceTrigger property to LostFocus. If you set this property, whenever the user change the value in FirstName or LastName textboxes and when the cursor focus moved out of the textbox, then the value will be automatically updated to the FirstName, LastName property in the Model.

We create new instance of the respective ViewModel during "View" construction and set that ViewModel instance to the DataCotext of the Grid.

<Grid x:Name="RegisterGrid">
 <Grid.ColumnDefinitions>
  <ColumnDefinition Width="50*"/>
  <ColumnDefinition Width="50*"/>
 </Grid.ColumnDefinitions>
 <StackPanel Orientation="Vertical">
  <TextBlock Name="txtblkFirstName" Text="First Name: "  Height="23" HorizontalAlignment="Right"/>
  <TextBlock Name="txtblkLastName" Text="Last Name: "  Height="23" HorizontalAlignment="Right"/>
 </StackPanel>
 <StackPanel Orientation="Vertical" Grid.Column="1">
  <TextBox Name="txtFirstName" Text="{Binding Customer.FirstName, UpdateSourceTrigger=LostFocus}" Width="120"  Height="23" HorizontalAlignment="Left"/>
  <TextBox Name="txtLastName" Text="{Binding Customer.LastName, UpdateSourceTrigger=LostFocus}" Width="120"  Height="23" HorizontalAlignment="Left"/>
 </StackPanel>
 <Button x:Name="btnSubmit" Content="Submit" Command="{Binding SubmitCommand}" Grid.Column="1" Width="50" Height="23" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid>

public partial class Register : Window
{
 public Register()
 {
  InitializeComponent();
  RegisterGrid.DataContext = new RegisterViewModel();
 }
}

In this RegisterViewModel, we are using SubmitCommand as a property, which is inherited from ICommand. In the constructor of RegisterViewModel, we are creating instance of SubmitCommand and passing the method "SaveCustomer" which is the ViewModel itself, as a constructor parameter to the SubmitCommand.

Once we bind this Submit Command with the Submit button in the View, then Submit button will be enabled or disabled based the value returned by the "CanExecute" method. If it is true then button would be enabled. Otherwise not.

When the Submit button clicked, call goes to Execute method in the SubmitCommand class. Since the SubmitCommand class hold the method reference(SaveCustomer) of the ViewModel, it will call the SaveCustmer method.

public class RegisterViewModel : INotifyPropertyChanged
{
 private CustomerDetail customer;
 public CustomerDetail Customer
 {
  get{ return customer; }
  set{ customer = value;
       OnPropertyChanged("Customer");}
 }
 private ICommand submitCommand;
 public ICommand SubmitCommand
 {
  get{ return submitCommand;}
  set{ submitCommand = value;
       OnPropertyChanged("SubmitCommand");}
 }
 public event PropertyChangedEventHandler PropertyChanged;
 private void OnPropertyChanged(string propertyName)
 {
  if (PropertyChanged != null)
  {
   PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
 }
 public RegisterViewModel()
 {
  customer = new CustomerDetail();
  submitCommand = new SubmitCommand(SaveCustomer);
 }
 private void SaveCustomer()
 {
  using(ICustomerRepository customerRep = new CustomerRepository)
  {
   customerRep.Save(Customer);
  }
 }
}

public class SubmitCommand : ICommand
    {
        Action _executeAction = null;
        public SubmitCommand(Action submitCommand)
        {
            _executeAction = submitCommand;
        }
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _executeAction.Invoke();
        }
    }

In this View, we are binding the TextBlock with "FullName" property which is directly in the ViewModel, not in the Model property. So, we can just bind with the FullName property

<Grid x:Name="CustomerGrid">
 <Grid.ColumnDefinitions>
  <ColumnDefinition Width="50*"/>
  <ColumnDefinition Width="50*"/>
 </Grid.ColumnDefinitions>
 <StackPanel Orientation="Vertical">
  <TextBlock Name="txtblkName" Text="Customer Name: " Height="23" HorizontalAlignment="Right"/>
 </StackPanel>
 <StackPanel Orientation="Vertical" Grid.Column="1">
  <TextBlock Name="txtFirstName" Text="{Binding FullName, UpdateSourceTrigger=LostFocus}" Width="120"  Height="23" HorizontalAlignment="Left"/>
 </StackPanel>
</Grid>

public partial class Customer : Window
{
 public Customer()
 {
  InitializeComponent();
  CustomerGrid.DataContext = new CustomerViewModel();
 }
}

In this CustomerViewModel, we do a small modification from the original data that came from Model object. Because we need to display the FullName and it is purely the requirement related to display, we did the concatenation operation to add FirstName and LastName that came from Model and displayed in View

public class CustomerViewModel : INotifyPropertyChanged
{
 private CustomerDetail customer;
 public CustomerDetail Customer
 {
  get{ return customer; }
  set{ customer = value;
       OnPropertyChanged("FullName")}
 }
 public string FullName
 {
  get{ return customer.FirstName + " " + customer.LastName; }
 }
 public event PropertyChangedEventHandler PropertyChanged;
 private void OnPropertyChanged(string propertyName)
 {
  if (PropertyChanged != null)
  {
   PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
 }
 public CustomerViewModel()
 {
  Customer = GetCustomerDetail();
 }
 private void SaveCustomer()
 {
  using(ICustomerRepository customerRep = new CustomerRepository)
  {
   customerRep.Get(1001);//1001 is customer id
  }
 }
}

Your answer

Preview

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.
Anti-spam verification:
To avoid this verification in future, please log in or register.
site design / logo / content © 2013 - 2015 pinfaq.com
...