Wednesday, November 10, 2010

Silverlight – MVVM design pattern

MVVM (Model-View-ViewModel) design pattern is popular in WPF (Windows Presentation Foundation) or Silverlight programming. MVVM is a variation of MVC (Model-View-Controller) and is useful for modern UI development.

Model is used for data access layer, business logic or object model for the domain of interest. Model should work alone, completely independent from UI and View/ViewModel.

View is purely UI part and it consists of various visual elements such as button, textbox. Practically View is designed in XAML in WPF or Silverlight.

ViewModel is the model of the View and it is most important component in MVVM pattern. ViewModel is the data binding source for View and it holds UI data and UI states. The data binding can be one-way or two-way. Using data binding and putting all data into ViewModel makes View to pure UI only component. Practically speaking, any event handlers for View shouldn’t exist in View code behind. Along with property binding, there is a command binding to send some action to ViewModel from View such as button click. ViewModel then can call an appropriate Model method.

Now, let take a simple example. First, let’s assume we have very simplified Model class here. Again Model is pure API layer, it should work independently without any help of View or ViewModel.

public class MyModel
{
  public void Run(string text)
  {
     if (string.IsNullOrEmpty(text)) return;


    // Do data processing
    // ...


   MessageBox.Show(text);
  }
}

For simplicity, ViewModel contains one property and one command. They are binding sources for View elements – one for textbox and the other for button in this example. This ViewModel also create an instance for MyModel class and use it in command class. INotifyPropertyChanged interface provides a way to notify its property change state to other component such as View.

public class MyViewModel : INotifyPropertyChanged
{
  private MyModel model;
  private string myText;


  public MyViewModel()
  {
     this.model = new MyModel();
  }


   #region INotifyPropertyChanged Members
   public event PropertyChangedEventHandler PropertyChanged;
   #endregion


   public string MyText // property binding
  {
     get { return this.myText; }
     set
     {
        this.myText = value;
        if (PropertyChanged != null)
       {
           PropertyChanged(this, new PropertyChangedEventArgs("MyText"));
       }
  }
}


  public ICommand MyCommand // command binding
 {
   get { return new MyRunCommand(this.model); }
  }
}


public class MyRunCommand : ICommand
{
    private MyModel model;


   public MyRunCommand(MyModel model)
   {
     this.model = model;
   }


   #region ICommand Members
   public event EventHandler CanExecuteChanged;
 
   public bool CanExecute(object parameter)
   {
     return true;
   }


   public void Execute(object parameter)
   {
       if (parameter != null && parameter.ToString().Length > 0)
      {
          this.model.Run(parameter.ToString());
       }
   }
   #endregion
}

Here is a XAML for View.

(1) Define a namespace for sample DLL – “local” is defined as MyDemo assembly.(2) Set UserControl.DataContext to ViewModel class name with namespace.(3) Add {Binding..} to each UI element

<UserControl x:Class="MyDemo.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"
mc:Ignorable="d"
d:DesignHeight="77" d:DesignWidth="322"
xmlns:local="clr-namespace:MyDemo">


<UserControl.DataContext><local:MyViewModel /></UserControl.DataContext>


<Grid x:Name="LayoutRoot" Background="Green" Height="72" Width="322">
<TextBox Text="{Binding MyText, Mode=TwoWay}" Margin="12,16,86,12" Width="224" Height="33" />
<Button Content="Run" Command="{Binding MyCommand}" CommandParameter="{Binding MyText}" Margin="244,16,12,12" Width="66" Height="33" /> </Grid>
</UserControl>

Note: If you type wrong Binding element name (ex: {Binding MyCommandX} ), Silverlight won’t display any error and simply won’t work.

No comments:

Post a Comment