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");
}
}
}
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(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ViewModelBase : INotifyPropertyChanged
{
public ViewModelBase()
{
}
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new 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.