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
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
The following code snippets are coming from WpfSimple application (available with tutorial)
- <Window x:Class="WpfSimple.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:WpfSimple"
- Title="MainWindow" Height="150" Width="370">
- <Window.DataContext>
- <local:MainWindowViewModel/>
- </Window.DataContext>
- <Grid>
- <Button Content="Click"
- Height="23"
- HorizontalAlignment="Left"
- Margin="77,45,0,0"
- Name="btnClick"
- VerticalAlignment="Top"
- Width="203"
- Command="{Binding ButtonCommand}"
- CommandParameter="Hai" />
- </Grid>
- </Window>
ViewModel class used here is MainWindowViewModel, which is the object set as the DataContext of a view.
- xmlns:local="clr-namespace:WpfSimple"
- ow.DataContext>
- <local:MainWindowViewModel/>
- /Window.DataContext>
The ViewModel
ViewModel class used over here is MainWindowViewModel.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows.Input;
- using System.Windows;
-
- namespace WpfSimple
- {
- class MainWindowViewModel
- {
- private ICommand m_ButtonCommand;
- public ICommand ButtonCommand
- {
- get
- {
- return m_ButtonCommand;
- }
- set
- {
- m_ButtonCommand = value;
- }
- }
- public MainWindowViewModel()
- {
- ButtonCommand=new RelayCommand(new Action<object>(ShowMessage));
- }
- public void ShowMessage(object obj)
- {
- MessageBox.Show(obj.ToString());
- }
- }
- }
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).
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows.Input;
-
- namespace WpfSimple
- {
- class RelayCommand : ICommand
- {
- private Action<object> _action;
- public RelayCommand(Action<object> action)
- {
- _action = action;
- }
- #region ICommand Members
- public bool CanExecute(object parameter)
- {
- return true;
- }
- public event EventHandler CanExecuteChanged;
- public void Execute(object parameter)
- {
- if (parameter != null)
- {
- _action(parameter);
- }
- else
- {
- _action("Hello World");
- }
- }
- #endregion
- }
- }
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
- public class Product:INotifyPropertyChanged
- {
- private int m_ID;
- private string m_Name;
- private double m_Price;
- #region INotifyPropertyChanged Members
- public event PropertyChangedEventHandler PropertyChanged;
- private void OnPropertyChanged(string propertyName)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- #endregion
-
- public int ID
- {
- get
- {
- return m_ID;
- }
- set
- {
- m_ID = value;
- OnPropertyChanged("ID");
- }
- }
- public string Name
- {
- get
- {
- return m_Name;
- }
- set
- {
- m_Name = value;
- OnPropertyChanged("Name");
- }
- }
- public double Price
- {
- get
- {
- return m_Price;
- }
- set
- {
- m_Price = value;
- OnPropertyChanged("Price");
- }
- }
- }
Another approach for binding ViewModel object as datacontext of View
- public partial class App : Application
- {
- protected override void OnStartup(StartupEventArgs e)
- {
- base.OnStartup(e);
- WpfMvvmTest.MainWindow window = new MainWindow();
- ProductViewModel VM = new ProductViewModel();
- window.DataContext = VM;
- window.Show();
- }
- }
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.
- class ProductViewModel
- {
- private IList<Product> m_Products;
- public ProductViewModel()
- {
- m_Products = new List<Product>
- {
- new Product {ID=1, Name ="Pro1", Price=10},
- new Product{ID=2, Name="BAse2", Price=12}
- };
- }
- public IList<Product> Products
- {
- get
- {
- return m_Products;
- }
- set
- {
- m_Products = value;
- }
- }
- private ICommand mUpdater;
- public ICommand UpdateCommand
- {
- get
- {
- if (mUpdater == null)
- mUpdater = new Updater();
- return mUpdater;
- }
- set
- {
- mUpdater = value;
- }
- }
- private class Updater : ICommand
- {
- #region ICommand Members
- public bool CanExecute(object parameter)
- {
- return true;
- }
- public event EventHandler CanExecuteChanged;
- public void Execute(object parameter)
- {
- }
- #endregion
- }
- }
From the following view XAML you can understand how to bind a collection of object to the list control.
- <Window x:Class="WpfMvvmTest.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow" Height="350" Width="525">
- <Grid Height="314">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto"/>
- <RowDefinition Height="*"/>
- <RowDefinition Height="Auto"/>
- </Grid.RowDefinitions>
- <ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Products}" >
- <ListView.View>
- <GridView x:Name="grdTest">
- <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" Width="100"/>
- <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100" />
- <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="100" />
- </GridView>
- </ListView.View>
- </ListView>
- <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="
- Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.ID}" />
- <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}" />
- <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}" />
- <Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" />
- <Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" />
- <Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" />
- <Button Content="Update" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="310,40,0,0" Name="btnUpdate"
- VerticalAlignment="Top" Width="141"
- Command="{Binding Path=UpdateCommad}"
- />
- </Grid>
- </Window>
So... I hope you can get a small insight about MVVM from this article.