Simple WPF MVVM Concepts






3.20/5 (4 votes)
This tip is for newbie developers.
DownLoad Link :: validations-WPF_MVVM.zip
Introduction
In this tip, users will learn simple WPF using MVVM architecture where I will implement simple WPF UserControl
s with the ViewModel-Model class property.
For the UI, I will use textbox
es with ListView DataGrid
buttons in MainWindow.xaml file which later I will bind it with properties of Model
Class.
The application is built with the aim to provide an overview of the many simple best practices used in .NET programming for the newbie developer.
Overview
:::: MVVM Data-Context ::::
One of the very useful features while designing WPF applications is DataContext
. In this application example, I am setting the DataContext
in the code and all the elements in the UI get to access the ViewModel
Class object.
Here I had taken the name of Model
Class as "Model
" and ViewModel
class as "UserViewModel
".
Using the Code
#### MainWindow.xaml.cs ####
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Assignments
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// For Input Data From TextBoxes UI to ViewModel
UserViewModel obj = new UserViewModel();
this.DataContext = obj;
//For Items to Load in Grid View
UserGrid.ItemsSource = obj._UsersList;
}
}
}
To understand the concept in a better way, I had taken some property in Model
Class like SapId
, Name
, Gender
, Num
& DOb
with get
-set
Property which I will bind with Data View-Model class through ObservableCollection
by creating its object of Model
Class named as "_UserList
".
Now we will set the Get
-Set
Property of Member Variables with INotifyProperty
changed. INotifyPropertyChanged
is an interface to provide the recent update from ViewModel
to UI in an application.
Command Binding is done with using ICommand
Interface of "Submit Button". Newbies usually get confused in MVVM model with Event Args of any UserControls
Event.
In order to avoid this ambiguity, I am creating an ICommand
Interface where I had binded Submit Button to "ClickCommand
" with Get
-Set
property in which I am calling a respective function to occur on clicking Submit Button.
#### ViewModel.cs ####
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Assignments
{
//:::: My View Model Class ::::
public class UserViewModel : INotifyPropertyChanged, IDataErrorInfo
{
public ObservableCollection<Model> _UsersList;
//Constructor for View Model Class
public UserViewModel()
{
_UsersList = new ObservableCollection<Model>();
}
// Get Set Properties for Member Variables
//Name
private string name;
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); }
}
//Gender
private string gender;
public string Gender
{
get { return gender; }
set { gender = value; NotifyPropertyChanged("Gender"); }
}
//SapiD
private string sapid;
public string SapId
{
get { return sapid; }
set { sapid = value; NotifyPropertyChanged("SapId"); }
}
//mOBILE nUMBER
private string num;
public string Num
{
get { return num; }
set { num = value; NotifyPropertyChanged("Num"); }
}
//Date of Birth
private DateTime? dob;
public DateTime? Dob
{
get { return dob; }
set { dob = value; NotifyPropertyChanged("Dob"); }
}
//:::: Command Bindings ::::
//:::: For Submit Button we had call MyAction() Function ::::
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
if( _clickCommand == null)
{
_clickCommand = new CommandHandler(() => MyAction(), true);
}
return _clickCommand;
}
}
public void MyAction()
{
// Write My Actions Commands Here
//Binding text Box property from UserViewModel & Model Classes
_UsersList.Add(new Model()
{ SapId = SapId, Name = Name, Gender = Gender, Dob = Dob, Num = Num });
Name = string.Empty;
SapId = string.Empty;
//Dob = DateTime.Now;
Gender = string.Empty;
Num = string.Empty;
}
//:::: For Clear Button Event we had call MyClear() function ::::
private ICommand _clickClear;
public ICommand ClickClear
{
get
{
if (_clickClear == null)
{
_clickClear = new CommandHandler(() => MyClear(), true);
}
return _clickClear;
}
}
public void MyClear()
{
// Write here Commands for Clear all Text boxes
Name = string.Empty;
SapId = string.Empty;
Gender = string.Empty;
Num = string.Empty;
}
// INotify PropertyChange Command
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
//For Displaying Errors as a String
public string Error
{
get
{
return this[string.Empty];
}
}
// String Errors Checking for TextBoxes
public string this[string columnName]
{
get
{
string result = null;
// Conditions for the TextBox Validations with Model Name Property
if (columnName == "Dob")
{
//For Empty
if (Dob == null)
{
result = "Please enter DOB";
return result.ToString();
}
//For Not selecting Present Date
if (Dob == System.DateTime.Now)
{
result = "Please enter valid DOB";
return result;
}
}
if (columnName == "Gender")
{
//For Empty
if (this.Gender == null)
result = "Please enter your Gender";
return result;
}
if (columnName == "SapId")
{
//For Empty
if (this.SapId == null)
{
result = "Please enter sapid";
return result;
}
}
if (columnName == "Name")
{
//For Empty
if (Name == null)
{
result = "Please enter your Name";
return result;
}
// For 50 Chars
if (Name.Length > 50)
{
result = "Name can not be longer than 50 chars";
return result;
}
//For Special Characters only
string st = @"!|@|#|\$|%|\?|\>|\<|\*";
if (Regex.IsMatch(Name, st))
{
result = "Special chars not allowed";
return result;
}
//For Characters only
string str = @"^[0-9]+$";
if (Regex.IsMatch(Name, str))
{
result = "Only chars are allowed";
return result;
}
}
if (columnName == "Num")
{
//For Empty
if (this.Num == null)
{
result = "Please enter Mobile Number";
return result;
}
// Not MAx than 10 digit
if (this.Num.Length > 10)
return "Number cannot be more than 10 digit";
//Only Number
string str = @"^[0A-Za-z]+$";
if (Regex.IsMatch(Num, str))
{
result = "Only Numbers are allowed";
return result;
}
}
return result;
}
}
} //End of UserView Class
//******** Icommand Interface Functions Starts Here **********
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
// **************************************************************************
//:::: My Model Class ::::
public class Model
{
public string name;
public string gender;
public DateTime? dob;
public String num;
public string sapid;
// ### GEt SEt PRoperties for Member Variables ###
public string Name
{
get{return name;}
set{name = value;}
}
public string Gender
{
get{return gender;}
set{gender = value;}
}
public DateTime? Dob
{
get{return dob;}
set{dob = value;}
}
public String Num
{
get{return num;}
set{num = value;}
}
public string SapId
{
get{return sapid;}
set{sapid = value;}
}
} // End Of Model Class
} // End Of NAmeSpace
In the above application, I took two ICommand
Interfaces - ClickCommand
& ClickClear
.
ClickCommand
is calling MyAction()
where I add data from UI to GridView
.
ClickClear
is calling MyClear()
where I make the values of all textboxes as null
except DOb
.
We can add numerous functions binded with any ICommand
Interface.
For ICommand
Interface to execute, there is a CommandHandler Class
in ViewModel.cs.
Now coming on to Validations, we have to take IDataErrorInfo
Interface with UserViewModel
Class because it is binded with DataContext
& INotifyPropertyChanged
.
In this String
Property, I am checking columnName
with my Property of Member Variables and using Regular Expressions for different Conditions as needed for Validations.
#### MainWindow.xaml ####
<Window x:Class="Assignments.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:Assignments"
Title="MainWindow" Height="463.433" Width="674.254">
<!--:::: For Errors Labels beside Text Boxes::::-->
<Window.Resources>
<ControlTemplate x:Key="eTemplate">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Foreground="White"
Background="Red" FontSize="13"
Text="{Binding ElementName=adorned,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" >
</TextBlock>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="adorned"/>
</Border>
</DockPanel>
</ControlTemplate>
</Window.Resources>
<Grid>
<Label Content="Assignment I --> Registration Form with Validations"
HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top"/>
<Label Content="Name" HorizontalAlignment="Left"
Margin="180,77,0,0" VerticalAlignment="Top"/>
<TextBox Text="{Binding Name, ValidatesOnDataErrors=true,
UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource
ResourceKey=eTemplate}" HorizontalAlignment="Left" Height="23"
Margin="250,80,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
<Label Content="SapId" HorizontalAlignment="Left"
Margin="180,107,0,0" VerticalAlignment="Top"/>
<TextBox Text="{Binding SapId,ValidatesOnDataErrors=true,
UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource
ResourceKey=eTemplate}" HorizontalAlignment="Left" Height="23"
Name="SapId" Margin="250,110,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
<Label Content="DOB" HorizontalAlignment="Left"
Margin="180,138,0,0" VerticalAlignment="Top"/>
<TextBox Text="{Binding Dob,ValidatesOnDataErrors=true,
UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource
ResourceKey=eTemplate}" HorizontalAlignment="Left" Height="23"
Name="Dob" Margin="250,142,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
<Label Content="Gender" HorizontalAlignment="Left"
Margin="177,177,0,0" VerticalAlignment="Top"
RenderTransformOrigin="0.652,0.516"/>
<TextBox Text="{Binding Gender,ValidatesOnDataErrors=true,
UpdateSourceTrigger=PropertyChanged}" Validation.ErrorTemplate="{StaticResource
ResourceKey=eTemplate}" HorizontalAlignment="Left" Height="23"
Name="Gender" Margin="250,180,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
<Label Content="Mobile No" HorizontalAlignment="Left"
Margin="173,208,0,0" VerticalAlignment="Top"/>
<TextBox Text="{Binding Num,ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource ResourceKey=eTemplate}"
HorizontalAlignment="Left" Height="23" Name="Num"
Margin="250,211,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="120"/>
<Button Command="{Binding ClickCommand}" Content="Submit"
HorizontalAlignment="Left" Margin="180,255,0,0"
VerticalAlignment="Top" Width="75"/>
<Button Command="{Binding ClickClear}" Content="Reset"
HorizontalAlignment="Left" Margin="312,255,0,0"
VerticalAlignment="Top" Width="75"/>
<!--:::: My Grid View ::::-->
<ListView Name="UserGrid" ItemsSource="{Binding _UsersList}"
RenderTransformOrigin="0.538,-1.94" Margin="73,285,141,19" >
<ListView.View>
<GridView x:Name="grdTest" AllowsColumnReorder="true">
<GridViewColumn Header="SapId"
DisplayMemberBinding="{Binding SapId}" Width="60"/>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding Name}" Width="55" />
<GridViewColumn Header="Gender"
DisplayMemberBinding="{Binding Gender}" Width="45" />
<GridViewColumn Header="DOB"
DisplayMemberBinding="{Binding Dob}" Width="150" />
<GridViewColumn Header="Number"
DisplayMemberBinding="{Binding Num}" Width="120" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
ScreenShots
This is how the application looks like on startup.
Solution Explorer
If you loved the way in which I explained to you, then stay tuned. I will soon be uploading more articles for you!!
I must Serve you to lead all ~ Sumit Anand