Geeks With Blogs
David Vallens

The MVVM (Model-View-ViewModel ) Design Pattern requires that you create a facade layer between the Model and the View called the ViewModel.

The ViewModel is supposed to supplement the functionality provided by the Model, and coerce it into a form the View can more readily consume. The result being; the View code can be pretty thin and concerned only with view based activities, while the Model can remain uncompromising by the demands placed on it by a View meaning it can better represent the data/concepts its supposed model.

Brilliant, all we need do now is knock together the ViewModel thing, sounds like a trivial code monkey task....

The Model Class

We'll start simple, our Model object has 2 properties Name & JobTitle, and supports the INotifyPropertyChanged interface, the implementation is not shown (in real life that's fine as it may not even be your Model objects your working with).

public class PersonModel : INotifyPropertyChanged
{
    private string _name;
    private string _jobTitle;

    public string Name
    {
        get ...
        set ...
    }

    public string JobTitle
    {
        get ...
        set ...
    }
        
    ...
}

Attempt 1

Your first version may look a little like this.

    public class PersonViewModel
    {
        private readonly PersonModel _personModel;

        internal PersonViewModel(PersonModel personModel)
        {
            if (personModel == null) throw new ArgumentNullException("personModel");
            _personModel = personModel;
        }

        public string Name
        {
            get { return _personModel.Name; }
            set { _personModel.Name = value; }
        }

        public string JobTitle
        {
            get { return _personModel.JobTitle; }
            set { _personModel.JobTitle = value; }
        }

        // Rince and repeat....
    }

Not bad, but when a property changes, the view wont know, and therefore will not update...

Attempt 2

This versions better, now when the View tries to hook the PropertyChanged event its actually hooking events on the Model, and when the property on the Model changes the View will know.

    public class PersonViewModel : INotifyPropertyChanged
    {
        private readonly PersonModel _personModel;

        internal PersonViewModel(PersonModel personModel)
        {
            if (personModel == null) throw new ArgumentNullException("personModel");
            _personModel = personModel;
        }

        public string Name
        {
            get { return _personModel.Name; }
            set { _personModel.Name = value; }
        }

        public string JobTitle
        {
            get { return _personModel.JobTitle; }
            set { _personModel.JobTitle = value; }
        }

        // Rince and repeat....

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged
        {
            add { _personModel.PropertyChanged += value; }
            remove { _personModel.PropertyChanged -= value; }
        }
        #endregion
    }

This solution works pretty well, but has 2 drawbacks:-

  • The recipient of the event sees the Model as the sender (not the ViewModel)
  • If you want to add properties that are not on the Model, then you have no way to fire the PropertyChanged event.

Attempt 3

Refactoring the event, solves both these problems, we now catch the events from the Model and forward them. As a result the sender is correct and we can fire our own events for our own properties. We can even rename events as they pass though if we want to change the name of the properties exposed by the Model.

public class PersonViewModel : INotifyPropertyChanged
{
    private readonly PersonModel _personModel;

    internal PersonViewModel(PersonModel personModel)
    {
        if (personModel == null) throw new ArgumentNullException("personModel");
        _personModel = personModel;
        _personModel.PropertyChanged += new PropertyChangedEventHandler(PersonModel_PropertyChanged);
    }

    public string Name
    {
        get { return _personModel.Name; }
        set { _personModel.Name = value; }
    }

    public string JobTitle
    {
        get { return _personModel.JobTitle; }
        set { _personModel.JobTitle = value; }
    }

    // Rince and repeat....

    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    void PersonModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        OnPropertyChanged(e.PropertyName);
    }
    #endregion
}

But we still have issues, if the Model is long lived, then each ViewModel that is created has its lifetime locked to the Model object. This is because the PropertyChanged event on the Model keeps a reference to the ViewModel.
Depending on the application this can cause massive memory leaks (Determining if this an issue is down to your specific application).

Attempt 4

We can solve the memory leak issue using the Weak Event Pattern.

public class PersonViewModel : INotifyPropertyChanged, IWeakEventListener
{
    private readonly PersonModel _personModel;

    internal PersonViewModel(PersonModel personModel)
    {
        if (personModel == null) throw new ArgumentNullException("personModel");
        _personModel = personModel;
        NotifyPropertyChangedEventManager.AddListener(_personModel, this);
    }

    public string Name
    {
        get { return _personModel.Name; }
        set { _personModel.Name = value; }
    }

    public string JobTitle
    {
        get { return _personModel.JobTitle; }
        set { _personModel.JobTitle = value; }
    }

    // Rince and repeat....

    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    #region IWeakEventListener Members
    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
    {
        PropertyChangedEventArgs pcArgs = e as PropertyChangedEventArgs;
        if (pcArgs != null)
        {
            OnPropertyChanged(pcArgs.PropertyName);
            return true;
        }
        return false;
    }
    #endregion
}

We also need to implement NotifyPropertyChangedEventManager

public class NotifyPropertyChangedEventManager : WeakEventManager
{
    public static NotifyPropertyChangedEventManager CurrentManager
    {
        get
        {
            var manager_type = typeof(NotifyPropertyChangedEventManager);
            var manager = WeakEventManager.GetCurrentManager(manager_type) as NotifyPropertyChangedEventManager;
            if (manager == null)
            {
                manager = new NotifyPropertyChangedEventManager();
                WeakEventManager.SetCurrentManager(manager_type, manager);
            }
            return manager;
        }
    }

    public static void AddListener(INotifyPropertyChanged source, IWeakEventListener listener)
    {
        CurrentManager.ProtectedAddListener(source, listener);
        return;
    }

    public static void RemoveListener(INotifyPropertyChanged source, IWeakEventListener listener)
    {
        CurrentManager.ProtectedRemoveListener(source, listener);
        return;
    }
    protected override void StartListening(object source)
    {
        ((INotifyPropertyChanged)source).PropertyChanged += Source_PropertyChanged; return;
    }
    protected override void StopListening(object source)
    {
        ((INotifyPropertyChanged)source).PropertyChanged -= Source_PropertyChanged;
        return;
    }

    void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        { CurrentManager.DeliverEvent(sender, e); };
    }
}

Finally this seems to solve all of our issues.

Conclusion

Wrappering Model objects is nowhere near as easy as it looks at first glance, in part 2 we will look at wrapping child objects, and in Part 3 collections.

Posted on Friday, April 1, 2011 5:08 AM WPF , MVVM | Back to top


Comments on this post: Building ViewModel Facade Objects - Part 1

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © David Vallens | Powered by: GeeksWithBlogs.net