Pages

Monday, May 21, 2012

MVVM sample using DataGrid control in WPF


Here is my first MVVM sample in WPF. I have prepared the sample using MVVM with the help of DataGrid in WPF.
The following sample will just showcase the information about the person in the MVVM pattern.


RecordInfo :- [ Model ]
Basically model will have only the data information. Model does not know anything about the ViewModel. It will just build the View through ViewModel.
It will have the following datas. Name, Age, DateOfBirth and Address.


 public class RecordInfo : ViewModelBase 
 {        
     string _name = string.Empty;        
     Int32 _age = 0;        
     DateTime _dob;        
     string _address = string.Empty;

     public RecordInfo()        
     {
                    
     }

     public string Name
     {
         get
         {
            return _name;  
         }
         set            
         {
            _name = value;
            OnPropertyChanged("Name");
         }
     }
     
     public Int32 Age
     {
         get  
         {  
            return _age;  
         }
         set 
         { 
            _age = value;  
            OnPropertyChanged("Age");
         } 
     }

     public DateTime DateOfBirth        
     {            
         get            
         {                
             return _dob;            
         }            
         set            
         {                
             _dob = value;                
             OnPropertyChanged("DateOfBirth");            
         }        
     }

     public string Address        
     {            
          get            
          {                
              return _address;            
          }            
          set            
          {                
              _address = value;                
              OnPropertyChanged("Address");            
          }        
     } 
 }

ViewModelBase :-

Both ViewModel and Model will have the ViewModelBase as the base class. ViewModelBase is nothing but the class will just notify property changes to the View through the INotifyPropertyChanged interface. Instead of inheriting the INotifyPropertyChanged interface to both ViewModel and Model. It has been used only in the ViewModelBase.

One interesting aspect of ViewModelBase is that it provides the ability to verify that a property with a given name actually exists on the ViewModel object. The below code adds this useful support to MVVM.


public class ViewModelBase : INotifyPropertyChanged 
{        
       public ViewModelBase()        
       {

       }
       public void OnPropertyChanged(string name)        
       {            
            if (PropertyChanged != null)            
            {                
                 PropertyChanged(thisnew PropertyChangedEventArgs(name));            
            }        
       }
       public event PropertyChangedEventHandler PropertyChanged;  
}

DataGridViewModel : [ ViewModel ]

ViewModel will have the observablecollection of elements to update the View with the Model class.  It always serves the data binding between View and Model. That means ViewMode acts as a DataBinder or Converter to communicate the information between View to Model through public properties and Delegate commands. As this class class is separated from the View (UI), these classes are relatively straightforward to unit tests.

public class DataGridViewModel : ViewModelBase
        {
            ObservableCollection<RecordInfo> infos;
            ICommand _command;
 
            public DataGridViewModel()
            {
                PersonsInfo = new ObservableCollection<RecordInfo>();
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "AA",
                    Age = 24,
                    DateOfBirth = new DateTime(1987, 4, 29),
                    Address = "XXX XXX XXXX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "BB",
                    Age = 23,
                    DateOfBirth = new DateTime(1988, 3, 4),
                    Address = "XXX XXXXX XXX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "CC",
                    Age = 26,
                    DateOfBirth = new DateTime(1985, 10, 2),
                    Address = "XXX XXX X"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "DD",
                    Age = 28,
                    DateOfBirth = new DateTime(1983, 5, 7),
                    Address = "XX XXXXX XX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "EE",
                    Age = 26,
                    DateOfBirth = new DateTime(1985, 11, 20),
                    Address = "XXXX XXXXX XXX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "FF",
                    Age = 22,
                    DateOfBirth = new DateTime(1989, 1, 27),
                    Address = "XXXXXXXXXXX XX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "GG",
                    Age = 21,
                    DateOfBirth = new DateTime(1990, 8, 6),
                    Address = "XXX XXXX XXXXXX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "HH",
                    Age = 23,
                    DateOfBirth = new DateTime(1988, 9, 9),
                    Address = "XX XXXXXXXXX XXX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "II",
                    Age = 24,
                    DateOfBirth = new DateTime(1987, 10, 18),
                    Address = "XXXXXXXXX XXXX"
                });
                PersonsInfo.Add(new RecordInfo
                {
                    Name = "JJ",
                    Age = 21,
                    DateOfBirth = new DateTime(1990, 11, 27),
                    Address = "XXXXX XXXXXXXX XX"
                });
            }
 
            public ObservableCollection<RecordInfo> PersonsInfo
            {
                get
                {
                    return infos;
                }
                set
                {
                    infos = value;
                    OnPropertyChanged("PersonsInfo");
                }
            }
 
            public ICommand RemoveCommand
            {
                get
                {
                    if (_command == null)
                    {
                        _command = new DelegateCommand(CanExecute, Execute);
                    }
                    return _command;
                }
            }
 
            private void Execute(object parameter)
            {
                int index = PersonsInfo.IndexOf(parameter as RecordInfo);
                if (index > -1 && index < PersonsInfo.Count)
                {
                    PersonsInfo.RemoveAt(index);
                }
            }
 
            private bool CanExecute(object parameter)
            {
                return true;
            }
        }
 
The above class contains the ObservableCollection of Model class which relatively updates the view with the respective information.

DelegateCommand:-

It enables the user to hook up UI interactions with code without tightly coupling with two. The controls in the UI aren’t intimately aware of the command logic they are connected with, and the command logic is not aware of the controls it will be associated with.
Basically RoutedCommands works great in certain scenarios, and are prevalent in WPF. The thing is, routed commands are not always great fit for MVVM development. Because if we used the RoutedCommand, we need to use a CommandBinding somewhere in the UI in order to connect a VisualElement to the respective command. So we would likely stick the usage the RoutedCommands in MVVM.
But with the help of DelegateCommand everything is simple. It will have CanExecute and Execute callbacks to achieve this. It executes the different method in the ViewModel.

public class DelegateCommand : ICommand
    {

        Predicate<object> canExecute;
        Action<object> execute;

        public DelegateCommand(Predicate<object> _canexecute,Action<object> _execute)
            :this()
        {
            canExecute = _canexecute;
            execute = _execute;
        }

        public DelegateCommand()
        {

        }

        public bool CanExecute(object parameter)
        {
            return canExecute == null ? true : canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            execute(parameter);
        }
    }

In this sample, DataGrid will updates the Rows based on the ItemsSource updates from the ViewModel.
Each and every Row will be having the Delete button which invokes the DelegateCommand to delete the SelectedRow from the DataGrid.

<DataGridTemplateColumn>
      <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
               <Grid>
                   <Button Content="Remove..." Margin="3" Command="{Binding Path=DataContext.RemoveCommand,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" 
                                        CommandParameter="{Binding}"/>
               </Grid>
          </DataTemplate>
      </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

It will execute the Remove action from the connected observable collection. That means , it just Remove the selected item from the collection. As ObservableCollection is ICollectionChanged interface, it updates the View at the time when collection modified in the ViewModel.



6 comments:

  1. Excellent Tutorial....! It helped me to understand the MVVM pattern.

    ReplyDelete
  2. thanks for tutorial ! It's great !

    ReplyDelete
  3. Smashing tutorial, exactly what I was looking for!

    ReplyDelete
  4. This is great. I was looking for a good tutorial on MVVM using the DataGrid and INotifyPropertyChanged. Great to have the full source code. Thank you!

    ReplyDelete
  5. Great...!
    In my case: I have one parameter namely "a" and it has Default, Min, Max, Current values and PFB.

    Model:
    Class
    {
    int a;
    }

    How can we achieve the same in MMVM...!

    ReplyDelete