Wednesday, March 24, 2010

Silverlight 4 and MVVM pattern with ICommand

MVVM (Model- View – View Model) is the WPF / Silverlight UI design Pattern code model. A certain guideline a developer should follows in order to achieve a more testable, debug gable, manageable, readable Application.
A zero code behind, yes no button click event hander in the code behind file, no form / windows load logic, no UI binding logic, No UI validation or type casting code in the code behind file. Yes i menn no code or logic in the UI mainpage.xaml.cs file.


Following features are used in order to achieve the MVVM pattern with no logic on the UI code behind file , and to make sure that the presentation layer and logic loosely couple.

1) Two way binding capability of WPF/Silverlight UI (XAML)
2) IValueConvert for binding (eg: string class converting to colour class)
3) INotification or Dependency Property
4) Data context
5) Static resources
6) Icommand for avoiding the code behind event handler.
7) Use of validation=true in xaml(presentation layer)


Before MVVM, the MVP pattern was famous for winform and wpf applications ( Though it never took the full advantage of the Two way binding feature of WPF). MVC pattern is still famous with Asp.net Application.
Draw back of the old fashion winform or wpf / Silverlight application (with no mvvm pattern)

1) The Presentation and the code behind logic were tightly coupled
2) If we change the UI control we have change the logic in the code behind
3) Cannot imagine of more than one view sharing the same logic.
4) Not possible to write a unit test for a code behind, as the
presentation layer is tightly couple to the control.
5) In normal WPF application the view (xaml) is just treated as a data
storage.


Advantages of MVVM pattern:

1) Proper layering of the view and the data. The data is not stored in
view, View is just for presenting the data.
2) Clean testable and manageable code.
3) NO code behind so the Presentation layer and the logic are loosely
coupled.
4) With Silverlight 4.0 we have a new Icommand Support


In this example we have a textbox displaying Personname and another textbox displaying PersonAge,and on a click of a button the age will increment by a year till 26... the code is done in MVVM pattern.


Step 1: Create a Silverlight Application in Viual studio 2010 and name it as “simplePersonandageMVVM”

Step 2: Once created add a class file and call it as “PersonModel.cs” and paste this code . (this would be the model thats is data object)


using System.ComponentModel;

namespace simplePersonandageMVVM
{
   
    public class PersonModel : INotifyPropertyChanged
    {
        string name;
        public string Name
        {
            get { return this.name; }
            set
            {
                this.name = value;
                fire("Name");

            }
        }
        int age;
        public int Age
        {
            get { return this.age; }
            set
            {
                this.age = value;
                fire("Age");
            }
        }
        public PersonModel() { }
        public PersonModel(string name, int age)
        {
            this.name = name;
            this.age = age;
        }

        public void fire(string x)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(x));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }


}


Step 3: Create another class call it as “PersonViewModel.cs” This would be the viewModel and will take the role of an adapter between view(presentation layer) and the Model(entity class).




namespace simplePersonandageMVVM
{
    public class PersonViewModel
    {
        public PersonModel p { get; set; }
        public PersonViewModel()
        {
            p = new PersonModel("prabjot", 20);                     
        }

        public ICommand GetPerson
        {
            get { return new GetPersonCommand(this); }
        }
        
        public void Increaseage(PersonModel d)
        {
            d.Age++;           
            string x = d.Age.ToString();           
            MessageBox.Show(x);       
        
        }
    }
}




Step 4: Create a Command object class implementing the ICOMMAND interface and call it as “GetPersonCommand.cs”. This class overrides both the Icommand methods. Here the method CanExecute() checks for the condition if its met then only the button click is enable. Where as the other method Execute() takes care of execution on button click.




namespace simplePersonandageMVVM
{
    public class GetPersonCommand : ICommand
    {
        PersonViewModel pincommand;
        public GetPersonCommand( PersonViewModel Pcame)
        { 
          pincommand= Pcame;
           
           
        }

        public bool CanExecute(object parameter)
        {
           if(pincommand.p.Age > 25)
           {
               return false ;
           }
        else
           {
               return true;
           }
            
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
           pincommand.Increaseage(pincommand.p);
        }
    }
}



Step 5: Finally your xaml file looks like this



<UserControl x:Class="simplePersonandageMVVM.MainPage"
    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:simplePersonandageMVVM"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.Resources>
        <local:PersonViewModel  x:Key="pkey" />
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot" Background="White" 
          DataContext="{Binding Source={StaticResource pkey}}" >
        <Grid Name="hi" DataContext="{Binding Path=p, Mode=TwoWay}">
            <TextBox Height="23" HorizontalAlignment="Left" Margin="53,30,0,0" 
                 Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Name, Mode=TwoWay}" />
            <TextBox Height="23" HorizontalAlignment="Left" Margin="53,68,0,0" Name="textBox2" 
                 Text="{Binding Path=Age, Mode=TwoWay}"  VerticalAlignment="Top" Width="120" />
            <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="53,112,0,0" Name="button1"
                VerticalAlignment="Top" Width="75"  Command="{Binding Path=DataContext.GetPerson, ElementName= LayoutRoot }" 
                    CommandParameter="{Binding Path=Age, ElementName=hi}"  />
      </Grid>
    </Grid>
      
</UserControl>