In part 1, I asked the question “Do I expose the model directly, or do I wrap each item in its own view model?” We looked at two plausible options for handling this and saw some code examples of each.

In part 2, I asked the question “Do I expose the model’s collections directly, or do I wrap each collection?” We saw the progression of code to implement this "brute-force". Then we quickly ran away screaming.

This installment (part 3) asks "How do I cleanly and easily wrap the model’s collections with collections of ViewModels?" We'll take a look at a couple LINQ extensions that should make this pretty simple and straight forward. We may even find that these frameworks solve other problems too!

 

EDIT: (5/7/2011) - I thought I should update this series to reflect my current thoughts. To that end, please refer to my posts on using converters for ViewModel Locators and a WPF Tales from the Trenches presentation. These posts describe an alternate approach that makes it simple to wrap a model collection with view models on an as needed basis. I very much like this approach and have been using it for over a year in a real world, multi-developer WPF application.

 

Source code for this article, including the code from part 2, part 3, and unit tests is available for download:

MvvmWrappers.zip

Background:

MVVM says that a ViewModel sits between the View (XAML) and the Model. The ViewModel (VM) exposes data from the model plus additional view-specific details that the view (V) can easily bind to. Ideally, the view can be constructed with zero code-behind. Pure XAML with bindings to manage data and UI state. This is all well and good.

Now, lets say that our model is reasonably real-world and has a entities that contain collections of other entities, which in turn contain collections of other entities. Maybe some of these even have references back to other trees of data. The classic example is customers and orders.

The Model:

Customer
    |_ Shipping Addresses (Collection)
    |_ Billing Address
    |_ Orders (Collection)
    |_ First Name
    |_ Last Name
    |_ Etc…

Step 2 - Revisited: Shipping Addresses (Collection)

We want to bind the shipping addresses into the view. This is a collection of shipping address objects, exposed by the model’s customer object. Previously, we created the ViewModel to expose the customer to the view.

In part 1, we left off with the following customer ViewModel implementation:

using System.ComponentModel;
using System.Collections.ObjectModel;
 
namespace MvvmWrappers.ViewModels
{
    public class CustomerVM : INotifyPropertyChanged
    {
        private Models.Customer _Customer = null;
 
        /// <summary>
        /// Constructor - Add an event handler for PropertyChanged.
        /// </summary>
        public CustomerVM(Models.Customer customer)
        {
            _Customer = customer;
            _Customer.PropertyChanged += new PropertyChangedEventHandler(_Customer_PropertyChanged);
        }
 
        /// <summary>
        /// Watch for changes in the underlying model and propagate those that
        /// are exposed by this ViewModel.
        /// </summary>
        void _Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "FirstName":
                case "LastName":
                    this.OnPropertyChanged(e.PropertyName);
                    break;
            }
        }
 
        /// <summary>
        /// Delegate the storage to the model.
        /// Also delegate the INotifyPropertyChanged handling to the model.
        /// </summary>
        public string FirstName
        {
            get { return _Customer.FirstName; }
            set { _Customer.FirstName = value; }
        }
 
        /// <summary>
        /// Delegate the storage to the model.
        /// Also delegate the INotifyPropertyChanged handling to the model.
        /// </summary>
        public string LastName
        {
            get { return _Customer.LastName; }
            set { _Customer.LastName = value; }
        }
        
        #region INotifyPropertyChanged
        ...
        #endregion
 
    }
 
}
 

The code in part 2 worked, but overwhelmed the implementation with complex “plumbing” code. So, we seek a better solution.

Enter Bindable LINQ (BLINQ) and Continuous LINQ (CLINQ). These are two frameworks on Codeplex that provide potential solutions to the problem. Both of them implement enhanced LINQ implementations that return a collection that implements INotifyCollectionChanged (like ObservableCollection). Furthermore, they keep that collection in sync with a separate “source” collection.

Bindable LINQ (BLINQ)

EDIT: 5/7/2011 - It appears that Bindable LINQ is not going to be maintained or supported. I recommend using Continuous LINQ (below) instead. The author also suggests looking into Obtics.

For example, if I have an ObservableCollection<Customer>, I could write a LINQ query that looked something like:

ObservableCollection<Models.Customer> customers = new ObservableCollection<MvvmBlinq.Models.Customer>();

IBindableCollection<Models.Customer> filteredCustomers = (
    from c in customers.AsBindable()
    where c.LastName.StartsWith("A")
    select c);

customers.Add(new MvvmBlinq.Models.Customer() { LastName = "Adams" });
customers.Add(new MvvmBlinq.Models.Customer() { LastName = "Benedict" });

Debug.Assert(filteredCustomers.Count == 1, "Filtered customers should have 1 and only 1 record.");

In the above code, you can see that we create an ObservableCollection to hold Customer objects. Initially it is empty. Next, we define a Linq query over that collection that selects customers with a last name that starts with the letter “A”. This query holds records in an IBindableCollection, which also implements INotifyCollectionChanged among other standard collection interfaces. Next, we add 2 customers to the underlying (source) customers ObservableCollection. Lastly, we check the filteredCustomers  linked collection.

Astute readers will notice that without the BLINQ provider, we would not expect to find anything in  filteredCustomers because it was created before we even added items to the customers collection. This is the gift of BLINQ and CLINQ. They will keep the second collection in sync with the underlying source collection.

You say, “So what…. how does this help me?”

Glad you asked! In our case of ViewModels, we want to expose the ShippingAddresses collection by creating a new collection of ShippingAddressVM ViewModels. Each one of these ShippingAddressVMs will wrap the underlying ShippingAddress model.

You say, “Code please…”

using System;
using System.ComponentModel;
using Bindable.Linq;

namespace MvvmBlinq.ViewModels
{
    public class CustomerVM : INotifyPropertyChanged
    {
        private Models.Customer _Customer = null;

        /// <summary>
        /// Constructor - Add an event handler for PropertyChanged.
        /// </summary>
        public CustomerVM(Models.Customer customer)
        {
            _Customer = customer;
            _Customer.PropertyChanged += new PropertyChangedEventHandler(_Customer_PropertyChanged);

            ShippingAddresses = (from sa in _Customer.ShippingAddresses.AsBindable()
                                 select new ShippingAddressVM(sa));
        }

        /// <summary>
        /// Watch for changes in the underlying model and propagate those that
        /// are exposed by this ViewModel.
        /// </summary>
        void _Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "ShippingAddresses":
                    // If this is set to a new collection, then we need to reset the binding here.
                    ShippingAddresses = (from sa in _Customer.ShippingAddresses.AsBindable()
                                         select new ShippingAddressVM(sa));
                    this.OnPropertyChanged(e.PropertyName);
                    break; 

                case "FirstName":
                case "LastName":
                    this.OnPropertyChanged(e.PropertyName);
                    break;
            }
        }

        /// <summary>
        /// A collection of shipping addresses, wrapped in a ShippingAddressVM ViewModel
        /// </summary>
        private IBindableCollection<ShippingAddressVM> _ShippingAddresses;
        public IBindableCollection<ShippingAddressVM> ShippingAddresses
        {
            get { return _ShippingAddresses; }
            set
            {
                if (value == _ShippingAddresses)
                    return;

                _ShippingAddresses = value;

                OnPropertyChanged("ShippingAddresses");
            }
        }

        /// <summary>
        /// Delegate the storage to the model.
        /// Also delegate the INotifyPropertyChanged handling to the model.
        /// </summary>
        public string FirstName
        {
            get { return _Customer.FirstName; }
            set { _Customer.FirstName = value; }
        }

        /// <summary>
        /// Delegate the storage to the model.
        /// Also delegate the INotifyPropertyChanged handling to the model.
        /// </summary>
        public string LastName
        {
            get { return _Customer.LastName; }
            set { _Customer.LastName = value; }
        }

        #region INotifyPropertyChanged
        ...
        #endregion

    }
}

Compared to our last attempt, this version is much simpler! We hook the ShippingAddresses ViewModel collection up to the Model in the constructor via the Bindable LINQ query. This query is a standard LINQ query, except for the additional “AsBindable()” extension method in the query. The query doesn’t filter out any records. Rather, it creates a new ShippingAddressVM instance to wrap each of the underlying ShippingAddress model instances.

Net Result: A synchronized, bindable, ViewModel-wrapped collection.

You may also note that we re-bind the BLINQ collection if the model’s ShippingAddress PropertyChanged notification fires. This is to support the case where the model’s collection of shipping addresses is completely replaced with a new collection.

Continuous LINQ (CLINQ)

The pattern for using CLINQ is very similar to BLINQ. The two frameworks are remarkably similar, although each has some unique features.

using System;
using System.ComponentModel;
using ContinuousLinq;

namespace MvvmClinq.ViewModels
{
    public class CustomerVM : INotifyPropertyChanged
    {
        private Models.Customer _Customer = null;

        /// <summary>
        /// Constructor - Add an event handler for PropertyChanged.
        /// </summary>
        public CustomerVM(Models.Customer customer)
        {
            _Customer = customer;
            _Customer.PropertyChanged += new PropertyChangedEventHandler(_Customer_PropertyChanged);

            ShippingAddresses = (from sa in _Customer.ShippingAddresses
                                 select new ShippingAddressVM(sa));
        }

        /// <summary>
        /// Watch for changes in the underlying model and propagate those that
        /// are exposed by this ViewModel.
        /// </summary>
        void _Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "ShippingAddresses":
                    // If this is set to a new collection, then we need to reset the binding here.
                    ShippingAddresses = (from sa in _Customer.ShippingAddresses
                                         select new ShippingAddressVM(sa));
                    this.OnPropertyChanged(e.PropertyName);
                    break; 

                case "FirstName":
                case "LastName":
                    this.OnPropertyChanged(e.PropertyName);
                    break;
            }
        }

        /// <summary>
        /// A collection of shipping addresses, wrapped in a ShippingAddressVM ViewModel
        /// </summary>
        private ReadOnlyContinuousCollection<ShippingAddressVM> _ShippingAddresses;
        public ReadOnlyContinuousCollection<ShippingAddressVM> ShippingAddresses
        {
            get { return _ShippingAddresses; }
            set
            {
                if (value == _ShippingAddresses)
                    return;

                _ShippingAddresses = value;

                OnPropertyChanged("ShippingAddresses");
            }
        }

        /// <summary>
        /// Delegate the storage to the model.
        /// Also delegate the INotifyPropertyChanged handling to the model.
        /// </summary>
        public string FirstName
        {
            get { return _Customer.FirstName; }
            set { _Customer.FirstName = value; }
        }

        /// <summary>
        /// Delegate the storage to the model.
        /// Also delegate the INotifyPropertyChanged handling to the model.
        /// </summary>
        public string LastName
        {
            get { return _Customer.LastName; }
            set { _Customer.LastName = value; }
        }

        #region INotifyPropertyChanged
        ...
        #endregion

    }
}

 

You’ll notice that the above code is almost identical to the BLINQ example. In fact, it is slightly simpler since CLINQ doesn’t require the “AsBindable()” call. CLINQ returns a “ContinuousCollection” or a “ReadOnlyContinuousCollection”. This output also supports the standard collection interfaces, including INotifyCollectionChanged. In this case, CLINQ sees that the query reshapes the data and returns a Read Only collection.

CLINQ vs. BLINQ vs. Others

So what is the difference between these frameworks? Kyle Lanser posted a nice answer to this question on StackOverflow. He indicates that they are very similar in some respects. Both projects come with source code and both have some pretty nice sample applications.

BLINQ includes the ability to walk the query tree and detect other objects & collections that support change notification. It will then monitor these to keep the bound collection fully in sync with the underlying collection and other objects in the LINQ query. (See the BLINQ site on CodePlex for further details.)

CLINQ appears to support the possibility for Bi-Directional synchronization in cases where the CLINQ query does not reshape the data, but simply filters it. In this case, it may be possible to make changes against the bound query and see those changes in the source query. I have not tested this yet since it doesn’t really help with MVVM model wrapping scenario. You can find Continuous LINQ on CodePlex.

There are other frameworks out there. The one that I’ve seen is called Obtics. This is another CodePlex project. It appears to be quite feature rich, but time did not permit me to investigate it all that much. My initial skimming of the project made me feel a bit overwhelmed. There is a lot of “stuff” in there, which prevented me from grasping it quickly.

Bi-Directional Sync?

The implementations above do not support bi-directionally synchronization of the source and the bound collection. Changes to the model will show up in the BLINQ collection, but not vice-versa.

In our case, this makes sense. We are projecting a new object from the LINQ query, which reshapes the data. A given projection may not have all the data needed to reverse the flow of data. This is the same behavior as a SQL Server view. SQL won’t let you insert into a view that pulls from multiple sources or restructures the data. (Yes, there are InsteadOf triggers, but that’s beside the point ;-)

So what do we do to get data back into the model?

I’m going to have to get back to you on that. The short answer is that you route that “command” or logic through your ViewModel, which in turn modifies the underlying model on your behalf. In many cases, this should be relatively straight forward. In cases where you need the View to be able to push data into the ViewModel’s bound collection, you may want to take a look at the code in part 2 of this series and see if you can morph that toward a workable solution.

Other Uses of BLINQ / CLINQ

These two frameworks offer some pretty compelling features in a couple other scenarios.

Filtering

WPF offers the CollectionView as an intermediary between your collection and the bound control. This supports some nice features for data navigation, filtering and grouping. This is a good option, but BLINQ and CLINQ offer an alternative that may be more or less appropriate depending on your scenario.

WPF’s CollectionViewSource implements filters by raising the Filter event. In that event, you indicate whether or not to include the current item in the view.

Using BLINQ / CLINQ, the filtering could be done in the ViewModel by exposing a bound collection that is backed by a single LINQ query which defines the filter.

Scalar Values

BLINQ has a nice feature that lets you create a scalar value output of a LINQ query that is bound to an underlying collection. This scalar value is kept in sync with the underlying collection automatically. The View could then bind to this value and it would update automatically whenever any records in the underlying collections changed the resulting aggregate.

Conclusion

We started with the question of whether to try and fully wrap the Model behind ViewModels. Part 1 showed two options. If you take the approach of exposing the Model to the View directly, then you may not need the structure outlined above.

However, if you decide that you do want to fully (or mostly) wrap the Model, then the above seems like a feasible approach. It is relatively straight forward to construct and maintain. Plus, the usage of LINQ provides a really powerful way to filter, aggregate, and reshape the data from the Model. Isn’t that the whole point of ViewModels anyway?

Drop a comment if you have an opinion or experience on the matter.

Cheers!