The MVVM Light framework is a set of components that helps to create WPF applications in Model-View-ViewModel (MVVM) pattern. It was created by Laurent Bugnion as a lightweight MVVM framework. In this article we will get ourself introduce to this framework by building a simple calculator application.
Introduction
The MVVM Light framework is a set of components that helps to create WPF applications in Model-View-ViewModel (MVVM) pattern. It was created by Laurent Bugnion as a lightweight MVVM framework. In this article we will get our self introduce to this framework by building a simple calculator application.
Create a WPF application
Open VS 2015 and create a WPF application. Then from the Nuget Package Manager, execute the below command
PM >Install-Package MvvmLight
This will add a ViewModel folder containing the MainViewModel.cs and the ViewModelLocator.cs file.
MainViewModel Class
This class contains properties that the main View can data bind to. This class inherits from ViewModelBase abstract class which is the base class for the ViewModel classes in the MVVM pattern. A typical MainViewModel Class looks as under
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace WpfApplication1.ViewModel
{
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real"
////}
}
}
}
ViewModelLocator Class
This class contains static references to all the view models in the application and provides an entry point for the bindings.
A typical ViewModelLocator Class looks as under
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
namespace WpfApplication1.ViewModel
{
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
////if (ViewModelBase.IsInDesignModeStatic)
////{
//// // Create design time view services and models
//// SimpleIoc.Default.Register<IDataService, DesignDataService>();
////}
////else
////{
//// // Create run time view services and models
//// SimpleIoc.Default.Register<IDataService, DataService>();
////}
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
Design the "MainWindow.xaml" as under
<Window x:Class="WpfApplication1.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:WpfApplication1"
DataContext="{Binding Main, Source={StaticResource Locator}}"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Label x:Name="lblCaption" Content="Calculator Demo Using MVVM Light" HorizontalAlignment="Left" Margin="37,10,0,0" VerticalAlignment="Top" Width="253"/>
<Label x:Name="lblNum1" Content="First Number" HorizontalAlignment="Left" Margin="27,57,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="txtNum1" HorizontalAlignment="Left" Height="23" Margin="128,57,0,0" TextWrapping="Wrap" Text="{Binding FirstNumber}" VerticalAlignment="Top" Width="162" />
<Label x:Name="lblNum2" Content="Second Number" HorizontalAlignment="Left" Margin="27,111,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="txtNum2" HorizontalAlignment="Left" Height="23" Margin="128,111,0,0" TextWrapping="Wrap" Text="{Binding SecondNumber}" VerticalAlignment="Top" Width="162"/>
<Button x:Name="btnAddition" Content="+" HorizontalAlignment="Left" Margin="27,168,0,0" VerticalAlignment="Top" Width="75" Command="{Binding Operation}" CommandParameter="+"/>
<Button x:Name="btnSubtraction" Content="-" HorizontalAlignment="Left" Margin="128,168,0,0" VerticalAlignment="Top" Width="75" Command="{Binding Operation}" CommandParameter="-"/>
<Button x:Name="btnMultiplication" Content="*" HorizontalAlignment="Left" Margin="226,168,0,0" VerticalAlignment="Top" Width="75" Command="{Binding Operation}" CommandParameter="*"/>
<Button x:Name="btnDivision" Content="/" HorizontalAlignment="Left" Margin="324,168,0,0" VerticalAlignment="Top" Width="75" Command="{Binding Operation}" CommandParameter="/"/>
</Grid>
</Window>
Running the application at this point yields
Then in the MainViewModel we need to define the properties viz. FirstNumber and SecondNumber of type string and also the Operation command which is of type ICommand as under
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
}
public string FirstNumber { get; set; }
public string SecondNumber { get; set; }
public ICommand Operation { get; private set; }
}
The purpose is that, we need to get the values of the two textboxes viz. txtNum1 and txtNum2 which are bind to these View Model properties. All the button commands will be triggered through the ICommand interface. It defines a command. It's the standard way to trigger a button action following MVVM pattern.
Next we need to implement the Operation command as under
public MainViewModel()
{
Operation = new RelayCommand<string>((arg) => DoCalculation(arg));
}
private void DoCalculation(string @operator)
{
var result = 0;
switch (@operator)
{
case "+": result = Convert.ToInt32(FirstNumber) + Convert.ToInt32(SecondNumber); break;
case "-": result = Convert.ToInt32(FirstNumber) - Convert.ToInt32(SecondNumber); break;
case "*": result = Convert.ToInt32(FirstNumber) * Convert.ToInt32(SecondNumber); break;
case "/": result = Convert.ToInt32(FirstNumber) / Convert.ToInt32(SecondNumber); break;
}
MessageBox.Show(result.ToString());
}
In the MVVM Light open-source toolkit, the ICommand implementation is called RelayCommand. It is a generic command whose sole purpose is to relay its functionality to other objects by invoking delegates. The default return value for the CanExecute method is 'true'. This class allows application to accept command parameters in the Execute and CanExecute callback methods.It looks as under
using System;
using System.Windows.Input;
namespace GalaSoft.MvvmLight.Command
{
public class RelayCommand<T> : ICommand
{
//Below two are the class constructor
public RelayCommand(Action<T> execute);
public RelayCommand(Action<T> execute, Func<T, bool> canExecute);
//end of class constructor
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter);
public virtual void Execute(object parameter);
public void RaiseCanExecuteChanged();
}
}
The constructor of this class has two parameters - The first parameter is compulsory. It is used for the Execute method that the ICommand interface requires. The second parameter is a delegate for the CanExecute method specified by ICommand. This delegate must return a Boolean. This parameter is optional.
In our case we are using the first Constructor. And in the DoCalculation method, we are performing the Calculator operation. The argument passed to the DoCalculation method from the view is via the CommandParameter.
Run the application and the output for the "Multiplication" operation is as under
Initially, the App.xaml contains an application wide instance of the ViewModelLocator class
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:WpfApplication1.ViewModel" />
</ResourceDictionary>
</Application.Resources>
</Application>
The ViewModelLocator class then gets the instance of the MainViewModel class by using the Service Locator Pattern.
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
The DataContext binding is then defered to the ViewModelLocator class in the MainWindow
Reference
MVVM Light Toolkit
MVVM - Messenger and View Services in MVVM
Conclusion
In this article we have seen the use of building MVVM application in WPF using MVVM Light framework. Hope this will be helpful. Thanks for reading. Zipped file attached.