As part of learning MVVM pattern I tried to search many sites and blogs and found most of them are explained in a complicated manner. After some research I cracked the very basic steps in MVVM pattern, and then trying to write MVVM tutorials for absolute beginners.
I don't think much more time or words spend for explaining various part of MVVM and the relationship between MVVM and WPF. If you go to the depth of WPF you will realize that MVVM is the best suitable pattern for WPF (You might not understand the difference between these two) .
As a formal procedure I am giving a simple diagram and definition for MVVM
1.gif
I start this tutorial with two examples, i.e WpfSimple.csproj and WpfMvvmTest.csproj
For the sake of simplicity, the first project (WpfSimple.csproj) avoids the Model object (an example with Model will come later).
In the example WpfSimple, the view contains only a Button and no code behind, but its click event is loosely bound with ViewModel. The bindings between view and ViewModel are simple to construct because a ViewModel object is set as the DataContext of a view. If property values in the ViewModel change, those new values automatically propagate to the view via data binding. When the user clicks a button in the View, a command on the ViewModel executes to perform the requested action.
The View
2.gif
The following code snippets are coming from WpfSimple application (available with tutorial)
  1. <Window x:Class="WpfSimple.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:local="clr-namespace:WpfSimple"
  5. Title="MainWindow" Height="150" Width="370">
  6. <Window.DataContext>
  7. <local:MainWindowViewModel/>
  8. </Window.DataContext>
  9. <Grid>
  10. <Button Content="Click"
  11. Height="23"
  12. HorizontalAlignment="Left"
  13. Margin="77,45,0,0"
  14. Name="btnClick"
  15. VerticalAlignment="Top"
  16. Width="203"
  17. Command="{Binding ButtonCommand}"
  18. CommandParameter="Hai" />
  19. </Grid>
  20. </Window>
ViewModel class used here is MainWindowViewModel, which is the object set as the DataContext of a view.
  1. xmlns:local="clr-namespace:WpfSimple"
  2. ow.DataContext>
  3. <local:MainWindowViewModel/>
  4. /Window.DataContext>
The ViewModel
ViewModel class used over here is MainWindowViewModel.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows.Input;
  6. using System.Windows;
  7. namespace WpfSimple
  8. {
  9. class MainWindowViewModel
  10. {
  11. private ICommand m_ButtonCommand;
  12. public ICommand ButtonCommand
  13. {
  14. get
  15. {
  16. return m_ButtonCommand;
  17. }
  18. set
  19. {
  20. m_ButtonCommand = value;
  21. }
  22. }
  23. public MainWindowViewModel()
  24. {
  25. ButtonCommand=new RelayCommand(new Action<object>(ShowMessage));
  26. }
  27. public void ShowMessage(object obj)
  28. {
  29. MessageBox.Show(obj.ToString());
  30. }
  31. }
  32. }
You can see an empty code behind file. If you click on the button it will prompt a message box, despite the lack of event handling methods in the views. When the user clicks on buttons, the application reacts and satisfies the user's requests. This works because of bindings that were established on the Command property of Button displayed in the UI. The command object acts as an adapter that makes it easy to consume a ViewModel's functionality from a view declared in XAML.
Command="{Binding ButtonCommand}"
CommandParameter="Hai"
RelayCommand
In this example RelayCommand class is used for implementing ICommad object. I think it's a simple and standard approach. One more common approach is also available by using nested inner class (will explain later).
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows.Input;
  6. namespace WpfSimple
  7. {
  8. class RelayCommand : ICommand
  9. {
  10. private Action<object> _action;
  11. public RelayCommand(Action<object> action)
  12. {
  13. _action = action;
  14. }
  15. #region ICommand Members
  16. public bool CanExecute(object parameter)
  17. {
  18. return true;
  19. }
  20. public event EventHandler CanExecuteChanged;
  21. public void Execute(object parameter)
  22. {
  23. if (parameter != null)
  24. {
  25. _action(parameter);
  26. }
  27. else
  28. {
  29. _action("Hello World");
  30. }
  31. }
  32. #endregion
  33. }
  34. }
Next, I am going to discuss MVVM with an example having the Model object. So I like talk bit about INotifyPropertyChanged Interface
INotifyPropertyChanged
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, which a property value has changed. The INotifyPropertyChanged interface contains an event called PropertyChanged. Whenever a property on a ViewModel / Model object has a new value, it can raise the PropertyChanged event to notify the WPF binding system of the new value. Upon receiving that notification, the binding system queries the property, and the bound property on some UI element receives the new value
From the example WpfMvvmTest project I am taking the following code
  1. public class Product:INotifyPropertyChanged
  2. {
  3. private int m_ID;
  4. private string m_Name;
  5. private double m_Price;
  6. #region INotifyPropertyChanged Members
  7. public event PropertyChangedEventHandler PropertyChanged;
  8. private void OnPropertyChanged(string propertyName)
  9. {
  10. if (PropertyChanged != null)
  11. {
  12. PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  13. }
  14. }
  15. #endregion
  16. public int ID
  17. {
  18. get
  19. {
  20. return m_ID;
  21. }
  22. set
  23. {
  24. m_ID = value;
  25. OnPropertyChanged("ID");
  26. }
  27. }
  28. public string Name
  29. {
  30. get
  31. {
  32. return m_Name;
  33. }
  34. set
  35. {
  36. m_Name = value;
  37. OnPropertyChanged("Name");
  38. }
  39. }
  40. public double Price
  41. {
  42. get
  43. {
  44. return m_Price;
  45. }
  46. set
  47. {
  48. m_Price = value;
  49. OnPropertyChanged("Price");
  50. }
  51. }
  52. }
Another approach for binding ViewModel object as datacontext of View
  1. public partial class App : Application
  2. {
  3. protected override void OnStartup(StartupEventArgs e)
  4. {
  5. base.OnStartup(e);
  6. WpfMvvmTest.MainWindow window = new MainWindow();
  7. ProductViewModel VM = new ProductViewModel();
  8. window.DataContext = VM;
  9. window.Show();
  10. }
  11. }
In the following code snippet used inner class approach for implementing ICommand interface i.e create a private nested class within the ViewModel class, so that the command has access to private members of its containing ViewModel and does not pollute the namespace.
The disadvantage of this approach is that it requires creation of a nested class that implements ICommand for each command exposed by a ViewModel which will increase the size of the ViewModel class.
  1. class ProductViewModel
  2. {
  3. private IList<Product> m_Products;
  4. public ProductViewModel()
  5. {
  6. m_Products = new List<Product>
  7. {
  8. new Product {ID=1, Name ="Pro1", Price=10},
  9. new Product{ID=2, Name="BAse2", Price=12}
  10. };
  11. }
  12. public IList<Product> Products
  13. {
  14. get
  15. {
  16. return m_Products;
  17. }
  18. set
  19. {
  20. m_Products = value;
  21. }
  22. }
  23. private ICommand mUpdater;
  24. public ICommand UpdateCommand
  25. {
  26. get
  27. {
  28. if (mUpdater == null)
  29. mUpdater = new Updater();
  30. return mUpdater;
  31. }
  32. set
  33. {
  34. mUpdater = value;
  35. }
  36. }
  37. private class Updater : ICommand
  38. {
  39. #region ICommand Members
  40. public bool CanExecute(object parameter)
  41. {
  42. return true;
  43. }
  44. public event EventHandler CanExecuteChanged;
  45. public void Execute(object parameter)
  46. {
  47. }
  48. #endregion
  49. }
  50. }
From the following view XAML you can understand how to bind a collection of object to the list control.
  1. <Window x:Class="WpfMvvmTest.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="MainWindow" Height="350" Width="525">
  5. <Grid Height="314">
  6. <Grid.RowDefinitions>
  7. <RowDefinition Height="Auto"/>
  8. <RowDefinition Height="*"/>
  9. <RowDefinition Height="Auto"/>
  10. </Grid.RowDefinitions>
  11. <ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Products}" >
  12. <ListView.View>
  13. <GridView x:Name="grdTest">
  14. <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" Width="100"/>
  15. <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100" />
  16. <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="100" />
  17. </GridView>
  18. </ListView.View>
  19. </ListView>
  20. <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="
  21. Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.ID}" />
  22. <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Name}" />
  23. <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Price}" />
  24. <Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" />
  25. <Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" />
  26. <Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" />
  27. <Button Content="Update" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="310,40,0,0" Name="btnUpdate"
  28. VerticalAlignment="Top" Width="141"
  29. Command="{Binding Path=UpdateCommad}"
  30. />
  31. </Grid>
  32. </Window>
So... I hope you can get a small insight about MVVM from this article.