Click here to Skip to main content
15,887,336 members
Articles / Desktop Programming / WPF
Tip/Trick

Simple WPF MVVM Concepts

Rate me:
Please Sign up or sign in to vote.
3.20/5 (4 votes)
30 Jun 2015CPOL2 min read 331.3K   191   4   2
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 UserControls with the ViewModel-Model class property.

For the UI, I will use textboxes 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.

Image 1

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 ####

C#
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 ####

C#
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 ####

XML
<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

Image 2

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

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
India India
All about me is that i am Quick Learner,Love to work in free Environment.
Core focus is to meet clients real business needs through initial consultation, followed by a communicative and collaborative approach.
Delivers applications using C# .net and MS SQL Server. Comfortable working independently, in a team, or mentoring to meets deadlines irrelevant of pressure

Comments and Discussions

 
GeneralMy vote of 3 Pin
clawton30-Jun-15 12:11
clawton30-Jun-15 12:11 
GeneralRe: My vote of 3 Pin
1sumitanand30-Jun-15 19:29
professional1sumitanand30-Jun-15 19:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.