Tuesday, December 1, 2009

Creating a basic MVVM Application

In one of my previous post I mentioned MVVM ( or Model-View-ViewModel ) , so what exactly is the idea behind it, and more importantly, what are the advantages?

Separating UI from Business logic

The main problem with a Silverlight application is that you have a whole bunch of UI code intermingled with a bunch of Business logic.  This makes the whole application hard to understand, hard to debug and hard to test. We would like to have a way to avoid this. 

A solution to this problem is the concept of databinding in Silverlight, this allows us to loosely couple business Logic ( ViewModel )to our UI ( View ) and display the data we want ( Model ).

To allow interaction with our ViewModel, we would also require some functionalities to be bound to some UI components such as buttons.  For this we can use Commands,  Silverlight 3 provides by default the ICommand interface, and the PRISM framework has made an implementation of this so we can define commands on our FrameworkElement using attached properties. 

In Silverlight 4 there will be support for Commands out of the box for the ButtonBase and Hyperlink classes, so we will simply be able to bind to the Command property directly.

Setting it up

To make a MVVM application we will have to define a View and a ViewModel, a practice I like to use is to create two folders in our application to group our View’s and ViewModel’s so we have a clear view were our UI and our business logic is located.

As an example we will create a simple UI with a listbox and a button that will add quotes to it and display the data when the last quote was added.

The XAML for this view is straightforward :

<StackPanel Orientation="Vertical">
 <ListBox Height="150"/>
 <TextBlock />
 <Button Content="Add Quote"/>
</StackPanel>

Notice that I have not yet defined any databinding here, we will first create our ViewModel and map our binding to this afterwards.

A ViewModel that wants changes in it to be propagated to the UI should implement INotifyPropertyChanged this will provide a NotifyPropertyChanged event that will have to be invoked whenever a property in our ViewModel changes.  We will use an ObservableCollection to bind our quotes, to automatically trigger changes when we add new quotes.

This is the code for our ViewModel

public class ViewModel : INotifyPropertyChanged

    public ViewModel() 
   
        //Initialize our properties 
        Quotes = new ObservableCollection<string>(); 
        AddQuoteCommand = new DelegateCommand<object>(onAddQuoteCommandExecute); 
    }

    /// <summary> 
    /// Command that will be triggered when a button was clicked and will add a quote to the Quotes collection 
    /// </summary> 
    public DelegateCommand<object> AddQuoteCommand { get; set; }  

    /// <summary> 
    /// The list of Quotes we will bind to our listbox 
    /// </summary> 
    public ObservableCollection<String> Quotes { get; set; }  

    private DateTime lastModified; 
    /// <summary> 
    /// When was the list last modified 
    /// </summary> 
    public DateTime LastModified 
   
        get { return lastModified; }
        set 
       
            lastModified = value
            InvokePropertyChanged("LastModified"); 
       
    }  

    /// <summary> 
    /// When the AddQuoteCommand is triggered, it execute run this method 
    /// </summary> 
    private void onAddQuoteCommandExecute(object parameter) 
   
        //Add a generated quote 
        Quotes.Add(QuoteFactory.Generate());
        //Set the last modified date 
        LastModified = DateTime.Now; 
    }  

    public event PropertyChangedEventHandler PropertyChanged; 
    /// <summary> 
    /// Invoke that a property was changed 
    /// </summary> 
    /// <param name="propertyName">The propertyname</param> 
    private void InvokePropertyChanged(string propertyName) 
   
        if (PropertyChanged != null)
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    }

}

Now we can start binding our ViewModel to our View, there are a few ways you could to this, you could set the DataContext of the View in Code, or use dependency injection to initialize the ViewModel.  But for this example I will just define an instance of the ViewModel in the XAML of the View in its DataContext. Now we can simple add the databinding to our ListBox ,our TextBlock and bind the Command to the Button.  Silverlight will take care of the rest. 

<ListBox Height="150" ItemsSource="{Binding Quotes}"/>
<TextBlock Text="{Binding LastModified}" />
<Button Content="Add Quote" Commands:Click.Command="{Binding AddQuoteCommand}"/>

Conclusion

This example illustrates that setting up MVVM is not very difficult and allows for a nice separation of UI code and business logic.  You can, using MVVM, very easily define logic on several controls without having to write spaghetti code with events.  You would simply bind to the changing property in the UI ( e.g. a checkbox that is checked ) and have your View adapt to it accordingly because the setter in the ViewModel can very easily trigger business logic to occur.

There are however cases where you cannot, or very difficultly, achieve this.  You can not, for example, bind to a DependencyObject ( which is possible in WPF ). This will throw a XAML error in Silverlight 3.  However, in Silverlight 4 it will be possible to bind to a DependencyObject, making life a bit easier for people implementing MVVM.

1 comment:

  1. Yup, 't is inderdaad mogelijk om te binden aan een dependency object in SL4. Let wel op dat je nergens code staan hebt om die onmogelijkheid in SL3 te omzeilen, want dan werkt het plots niet meer in SL4 - I learned about that one the hard way ;-)

    ReplyDelete