Overview

When creating view models we are often tasked with the tedious process of creating a wrapper around our business objects. Sometimes this wrapping is used to restrict the number of properties available to the view and usually to add some extra properties to facilitate better user interaction. If our business objects are editable, or subject to change, we will have to listen to their INotifyPropertyChanged messages and retransmit these to the user interface. The other task that can take a fair amount of time is the wrapping, or sychronization, of collections. The MVVM Wrapper Kit was created for the purpose of dealing with these two scenarios, to greatly speed the creation of View Models.

Wrapping Business Objects

The NotificationObject is a base class that is used to wrap a business object. Any properties that the business object has that also exist on the ViewModel object will be retransmitted. Below is a simple example. Here we are using NotificationObject as a base class for a ViewModel class PersonViewModel. This 'wraps' the business object Person. We are only declaring one property, Name. Every time the business object (which implements INotifyPropertyChanged) has a change to its Name property, NotificationObject will raise NotifyPropertyChanged("Name"), because the PersonViewModel class also has a property with the same name. (Reflection is used to determine this)


  public class PersonViewModel : NotificationObject {

      private Person _person;

      public PersonViewModel(Person person)
         : base(person) {
         _person = person;
      }

      /// <summary>
      /// Gets and Sets the Name, this gets/sets the property from the business object that is wrapped
      /// </summary>
      /// <remarks>
      /// Notice how no NotifyPropertyChanged call is needed, that is all handled by the base class
      /// When the base class' property changes, because this view model object has a property of the same name
      /// the base class will re-raise the NotifyPropertyChanged
      /// </remarks>
      public String Name {
         get {
            return _person.Name;
         }
         set {
            _person.Name = value;
         }
      }
   }


The example above is extremely simplified, in a typical view model we would add some interaction logic and some extra properties.

Wrapping Observable Collections

When building View Models we often need to tranlsate a collection of business objects to a collection of View Model objects that wrap their business counterparts. At the same time we need to maintain collection sychronization, when the business collection gets an element added or removed from it we need to add or remove the corresponding View Model object.

The WrappedObservableCollection is a base class that is used to wrap ObservableCollections. In the example below we are creating a collection to wrap an ObservableCollection<Person>. The base class constructor takes one parameter, a delegate method used to wrap the Person business object and return a PersonViewModel. The only other step needed for basic operation is to assign the SourceCollection property. When the collection is first assigned for any Person objects the delegate method will be called to create the ViewModel objects. After this point the ICollectionChanged is monitored and View Model items are added and removed as needed.

   public class PersonViewModelCollection : WrappedObservableCollection<Person, PersonViewModel> {
      
      
      public PersonViewModelCollection(ObservableCollection<Person> personBusinessCollection)
         : base((Person person) => {
            //this is the method used to 'wrap' a person object in a PersonViewModel object
            return new PersonViewModel(person);
         }) {

         //now set the source collection, the base class will call the method above for any items in the collectionat this time
         //also for any items added after 
         base.SourceCollection = personBusinessCollection;
      }
   }


There is also a couple of methods on the base class that might be useful, any of these can be overriden to provide more feedback and specialised processing when items are added and removed.

      /// <summary>
      /// Method is called before the wrapped item is added to the collection
      /// </summary>
      /// <param name="itemConstructed">item that is about to be added to this collection</param>
      protected virtual void OnItemConstruction(TWrapped itemConstructed) { }

      /// <summary>
      /// Method is called after the wrapped item is added to the collection
      /// </summary>
      /// <param name="itemConstructed">item that is about to be added to this collection</param>
      protected virtual void OnItemConstructed(TWrapped itemConstructed) { }

      /// <summary>
      /// Method is called after the wrapped item is removed from the collection
      /// </summary>
      /// <param name="itemRemoved">item that has been removed</param>
      protected virtual void OnItemRemoved(TWrapped itemRemoved) { }

Last edited Feb 19, 2010 at 5:56 AM by aranmulholland, version 11

Comments

No comments yet.