Introduction
Software architecture is very important. Without implementing software architecture, UI code can be tightly coupled with backend code. This increases the cost of maintenance and makes our code Spaghetti, as lines of code start increasing with time. To address this concern, there are many software architectures that are available, such as Model -View - Controller (MVC), Model-View-Presenter (MVP), Model-View-ViewModel (MVVM), Model-View-Update (MVU), etc. Each pattern has its own advantages. Design patterns evolve with time and we start using more modern patterns as per skills and team size. Mostly, enterprise applications are developed using architecture that makes code well maintained and easy to work.
In this tutorial, we are going to discuss MVVM Architecture. This software architecture pattern helps us to address the Separation of Concern. It separates the GUI design from the application's backend, making it loosely coupled. It is used mostly in developing client applications. Let's understand each component of MVVM.
Model
The model represents the domain model also considered as Business Logic / Data Access Logic or we can abstractly define it as the backend of the application. They hold application data.
View
View Represents the UI of the application. This is what the user interacts with. It is the presentation part.
ViewModel
It is the logic of View. The ViewModel is also called as presentation logic. The View and ViewModel communicate with each other. The Request from ViewModel is forwarded to Model / Business Logic Layer / Data Access Layer. It allows sharing of computed/resultant data to the view.
There are popular frameworks that provide MVVM Architecture, such as
- DotVVM: Component-based MVVM Architecture for ASP.NET.
- MVVM Light Toolkit: The popular toolkit to build client applications in XAML.
- Knockout.js: Dynamic JavaScript UIs with the Model-View-View Model (MVVM) pattern.
- Vue.js: A popular Javascript framework.
And much more...
Implementing Basic MVVM Architecture in WPF
We are going to implement MVVM architecture in WPF. The Building Blocks of MVVM architecture in WPF are below. Here is a bit of an overview of each.
- ICommand: This interface provides the code contract for the commanding behaviour of Runtime XAML. It controls the behaviour such as enable, disable of control and behaviour on execution.
- INotifyPropertyChanged: This interface defines the contract for behaviour when the value of a property changes. It raises PropertyChanged event when the value changes.
- Binding to Property: It’s for data Binding, helps to easily present data in View. It has four components named.
- A binding target object.
- A target property.
- A binding source.
- A path to the value in the binding source to use.
It offers data flow in four ways
- OneWay
- TwoWay
- OneWayToSource
- OneTime
Implementation flow
Code Walkthrough
We will create a generic ICommand Implementation and name it RelayCommand. It will contain system-defined delegates Func and Action.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
namespace WpfAppMvvm.RelayCommands
{
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly Func<object, bool> canExecute;
private readonly Action<object> execute;
public RelayCommand(Func<object, bool> canExecute, Action<object> execute)
{
this.canExecute = canExecute;
this.execute = execute;
}
public bool CanExecute(object parameter)
{
return canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
}
We will implement INotifyPropertyChanged in ViewModel, it will be implemented for every View that will be created. In this code, we will enable the submit button based on conditions. We will invoke the OnProperyChanged() method on the property set. This method contains an invocation of a PropertyChanged event.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using WpfAppMvvm.RelayCommands;
namespace WpfAppMvvm.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
CheckAndEnableButton();
OnPropertyChanged();
}
}
private string phoneNumber;
public string PhoneNumber
{
get { return phoneNumber; }
set
{
phoneNumber = value;
CheckAndEnableButton();
OnPropertyChanged();
}
}
public ICommand SubmitButtonCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowViewModel()
{
CheckAndEnableButton();
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public void CheckAndEnableButton()
{
bool isEnabled = false;
string result = string.Empty;
if (Name?.Length > 0 && PhoneNumber?.Length > 0)
{
isEnabled = true;
result = string.Format("My Details are:\n{0}\n{1}\n", Name, PhoneNumber);
}
else
{
isEnabled = false;
}
SubmitButtonCommand = new RelayCommand((ob) => { return isEnabled; }, (ob) => { MessageBox.Show(result); });
OnPropertyChanged("SubmitButtonCommand");
}
}
}
Bind the ViewModel with View through DataContext and name the Properties the same as defined in ViewModel.
<Window x:Class="WpfAppMvvm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppMvvm"
xmlns:vm="clr-namespace:WpfAppMvvm.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="StackPanel">
<Setter Property="Margin" Value="20"/>
</Style>
<Style TargetType="Button">
<Setter Property="Margin" Value="10"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="10,5"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10,5"/>
<Setter Property="TextAlignment" Value="Center"/>
</Style>
</Window.Resources>
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<TextBlock Text="Name"/>
<TextBox Width="500" Text="{Binding Name}"/>
<TextBlock Text="Phone Number"/>
<TextBox Width="500" Text="{Binding PhoneNumber}"/>
<Button Width="200" Command="{Binding SubmitButtonCommand}">Submit</Button>
</StackPanel>
</Window>
Summary
In this tutorial, we learned about the importance of architecture, MVVM architecture, and the implementation of MVVM architecture in WPF.
Please feel free to share your thoughts, ideas and suggestions. Thanks for reading!!