Introduction
In this article, I would like to explain more about Model-View-ViewModel (MVVM) design pattern, why MVVM has to be used, and how to work with SQL Server using the MVVM design Pattern.
What is MVVM?
The MVVM pattern includes three key parts:
Model
(Business rule, data access, model classes)
View
(User interface (XAML))
ViewModel
(Agent or middle man between view and model)
The ViewModel
acts as an interface between Model
and View
. It provides data binding between View
and model data as well as handles all UI actions by using command.
The View
binds its control value to properties on a ViewModel
, which, in turn, exposes data contained in Model
objects.
Why use MVVM?
In traditional UI development - developer used to create a View
using window or user control or page and then write all logical code (Event handling, initialization and data model, etc.) in the code behind and hence they were basically making code as a part of view definition class itself. This approach increased the size of the View
class and created a very strong dependency between my UI and data binding logic and business operations. In this situation, no two developers can work simultaneously on the same view and also one developer's changes might break the other code. So everything is in one place is always a bad idea for maintainability, extendibility and testability prospective. So if you look at the big picture, you can feel that all these problems exist because there is a very tight coupling between the following items:
View
(UI)
Model
(Data displayed in UI)
- Glue code (Event handling, binding, business logic)
In MVVM, the Glue code is View Model. So it basically focuses on separation of concerns to make the implementation of an application structure a lot simpler to create as well as maintain.
Getting Started
Step 1
Create a new class and add the following code to create a delegate command:
public class DelegateCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public DelegateCommand(Action execute)
: this(execute, () => true)
{
_execute = execute;
}
public DelegateCommand(Action execute, Func<bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute();
}
public bool CanExecute(object parameter)
{
return _canExecute();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
Introduction to the ICommand Interface
Commands provide a mechanism for the view to update the model in MVVM architecture. Commands provide a way to search the element tree for a command handler. The ICommand
interface is defined inside System.Windows.Input
namespace. It has two methods and an event.
public bool CanExecute(object parameter)
public void Execute(object parameter)
public event EventHandler CanExecuteChanged
Execute
method is only invoked when CanExecute
returns true
. In case CanExecute
method returns false
, then the binding control disables automatically.
In order to know CanExecute
value listen to CanExecuteChanged
event, that may vary based on parameter passed.
Task of CanExecuteChanged
CanExecuteChanged
notifies any command sources (like a Button
or CheckBox
) that are bound to that ICommand
that the value returned by CanExecute
has changed. Command sources care about this because they generally need to update their status accordingly (e.g., a Button
will disable itself if CanExecute()
returns false
).
The CommandManager.RequerySuggested
event is raised whenever the CommandManager
thinks that something has changed that will affect the ability of commands to execute. This might be a change of focus.
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Step 2
Create a new class and add the following code to create a ViewModel
:
public class CreateEmployeeViewModel : INotifyPropertyChanged
{
private string _id;
private string _firstName;
private string _address;
public CreateEmployeeViewModel()
{
SaveCommand = new DelegateCommand(Save, () => CanSave);
}
public string ID
{
get { return _id; }
set
{
_id = value;
NotifyOfPropertyChange("ID");
}
}
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyOfPropertyChange("FirstName");
}
}
public string Address
{
get { return _address; }
set
{
_address = value;
NotifyOfPropertyChange("Address");
}
}
public ICommand SaveCommand { get; private set; }
public bool CanSave
{
get { return !string.IsNullOrEmpty(ID) && !string.IsNullOrEmpty(FirstName); }
}
string connectionString =
@"Data Source=RAVINDRA\MSSQLSERVERS;Initial Catalog=CrudMethod;Integrated Security=SSPI;";
public void Save()
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = con.CreateCommand();
cmd.CommandText = "INSERT INTO Users(ID,FirstName,Address)VALUES(@ID,@FirstName,@Address)";
cmd.Parameters.AddWithValue("@ID", ID);
cmd.Parameters.AddWithValue("@FirstName", FirstName);
cmd.Parameters.AddWithValue("@Address", Address);
try
{
con.Open();
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
throw ex;
}
finally
{
con.Close();
}
MessageBox.Show("Data Saved Successfully.");
}
Introduction to INotifyPropertyChanged Interface
The INotifyPropertyChanged
interface is used to notify clients, typically binding clients that a property value has changed.
For example, consider a Person
object with a property called FirstName
. To provide generic property-change notification, the Person
type implements the INotifyPropertyChanged
interface and raises a PropertyChanged
event when FirstName
is changed.
For change notification to occur in a binding between a bound client and a data source, your bound type should either:
- Implement the
INotifyPropertyChanged
interface (preferred)
- Provide a change event for each property of the bound type
Do not do both.
Step 3
In MainWindow.xaml.cs, add the following code to initialize the ViewModel
class and wrap the ViewModel
to the View
using the DataContext
property.
DataContext = new CreateEmployeeViewModel();
Step 4
Add the following code in MainWindow.xaml:
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Helloworld" Height="350" Width="525">
<Grid>
<Button x:Name="btnSave"
HorizontalAlignment="Left"
TabIndex="100"
Content="Save"
Width="100"
Height="30"
Margin="100,0,100,0"
Command="{Binding Path= SaveCommand}" />
<TextBox Height="23"
HorizontalAlignment="Left" Margin="114,26,0,0"
Name="txtID" VerticalAlignment="Top"
Width="120" Text="{Binding ID}"/>
<TextBox Height="23"
HorizontalAlignment="Left" Margin="114,55,0,0"
Name="txFirstName" VerticalAlignment="Top"
Width="120" Text="{Binding FirstName }" />
<TextBox Height="23"
HorizontalAlignment="Left" Margin="114,84,0,0"
Name="txtAddress" VerticalAlignment="Top"
Width="120" Text="{Binding Address}" />
<Label Content="ID:" Height="28"
HorizontalAlignment="Left"
Margin="84,26,0,0" Name="label1"
VerticalAlignment="Top" />
<Label Content="First Name:" Height="28"
HorizontalAlignment="Left"
Margin="40,50,0,0" Name="label2"
VerticalAlignment="Top" />
<Label Content="Address:" Height="28"
HorizontalAlignment="Left"
Margin="53,75,0,0" Name="label3"
VerticalAlignment="Top" />
</Grid>
</Window>
Note: Please look at Button Command property we are binding SaveCommand
to communicate with the ICommand
when the button was clicked.