Introducing MicroModels

It seems that every WPF developer has written a Model-View-ViewModel library, and I was starting to feel left out. Having written an MVC and MVP framework, I figured I may as well write an MVVM library. But I want it to be different - I want the ViewModels to be as small as possible. That's how MicroModels was born.

MicroModels is inspired by Fluent NHibernate and uses TypeDescriptors to dynamically define properties, collections and commands.

In the example below, the view model exposes the FirstName and LastName properties of the customer object. The LastName property is renamed to Surname, and a FullName property is defined using the two names. It also exposes a Save ICommand property that saves the customer to the repository.

public class EditCustomerModel : MicroModel
{
    public EditCustomerModel(Customer customer, CustomerRepository customerRepository)
    {
        Property(() => customer.FirstName);
        Property(() => customer.LastName).Named("Surname");
        Property("FullName", () => string.Format("{0} {1}", customer.FirstName, customer.LastName));
        Command("Save", () => customerRepository.Save(customer));
    }
}

What? That's it? Where's the INotifyPropertyChanged? The getters and setters? Don't worry, it's all done at runtime by MicroModels :)

The dynamic properties are available for data binding in XAML:

<DockPanel>
    <ToolBar DockPanel.Dock="Top">
        <Button Content="Save" Command="{Binding Path=Save}" />
    </ToolBar>

    <Border Background="#f0f0f0">
        <StackPanel>
            <WrapPanel Margin="1">
                <Label Margin="1" Width="130">FirstName</Label>
                <TextBox Margin="1" Width="50" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
            </WrapPanel>
            <WrapPanel Margin="1">
                <Label Margin="1" Width="130">Surname</Label>
                <TextBox Margin="1" Width="200" Text="{Binding Path=Surname, UpdateSourceTrigger=PropertyChanged}" />
            </WrapPanel>
            <WrapPanel Margin="1">
                <Label Margin="1" Width="130">Full Name</Label>
                <Label Margin="1" Width="200" Height="50" Content="{Binding Path=FullName}" />
            </WrapPanel>
        </StackPanel>
    </Border>
</DockPanel>

Dependency Analysis

MicroModels makes use of expression trees to analyse the dependencies a property has. In the code example above, MicroModels can figure out that the FullName property depends on the FirstName and LastName properties. If the customer raises a PropertyChanged event for either of those properties, MicroModels will raise a property changed event for FullName.

Multiple Sources

Perhaps instead of a view model exposing properties from a single object, your ViewModel will union multiple objects together. That's easy:

public class CompareCustomersModel : MicroModel
{
    public CompareCustomersModel(Customer left, Customer right)
    {
        AllProperties(left).WithPrefix("Left");
        AllProperties(right).WithPrefix("Right");
    }
}

From XAML you could bind to properties such as LeftFirstName and RightFirstName.

Wrapping Child ViewModels

If you had Order and LineItem business objects, it's common to create an OrderViewModel and LineItemViewModel, and to expose the Order's LineItems as a collection of LineItemViewModel's.

With MicroModels, this is no longer necessary. MicroModels can expose a collection of child items, and automatically wrap each item in a MicroModel. The example below shows how LineItems might be exposed as a collection, and adds a LineTotal property to each child item:

public class InvoiceViewModel : MicroModel
{
    public InvoiceViewModel(Order order, IEnumerable<LineItem> lineItems, IOrderService orderService)
    {
        AllProperties(order);

        Collection("LineItems", () => lineItems)
            .Each((item, model) => model.Property("LineTotal", () => item.UnitPrice * item.Quantity));

        Command("Save", () => orderService.Save(order, lineItems));
    }
}

When the Quantity of a single LineItem changes, MicroModels detects the changes and raises an event for the dynamic LineTotal property.

Get Started

You can download the binaries and reference MicroModels.dll. ViewModels just have to inherit from the MicroModel base class. You can also check out the source which includes a couple of samples. There is some work to do around documentation, improving the extensions system and cleaning it up. The public API needs some work, and I'd be really interested in what people think about some of the naming conventions used.

A picture of me

Welcome, my name is Paul Stovell. I live in Brisbane and work on Octopus Deploy, an automated deployment tool for .NET applications.

Prior to founding Octopus Deploy, I worked for an investment bank in London building WPF applications, and before that I worked for Readify, an Australian .NET consulting firm. I also worked on a number of open source projects and was an active user group presenter. I was a Microsoft MVP for WPF from 2006 to 2013.

15 Jan 2010

We want the Func
Give up the Func
We need the Func
Gotta have the Func

Consider this a positive review for MicroModels. Feel free to use it on any promotional materials.

(Blog comments on a Friday afternoon : where musical references and geek collide.)

15 Jan 2010

Nice one Steve, Parliament references! (Or should that be Parliamen<T> ? doubly bad geek humour there)

Paul, could you add in some auto / convention based mapping ? Then it could be even smaller.

15 Jan 2010

@Damian, there's an AllProperties() method to map all properties on an object - e.g.,:

AllProperties(customer);

Instead of:

Property(() => customer.FirstName);
Property(() => customer.LastName);
...

There are also some methods like WithPrefix and Exclude to further change how AllProperties works. Are there other conventions you would like to see?

Rob
Rob
15 Jan 2010

Nice work. This may come as a shock though: TypeDescriptors don't work in Silverlight. The binding engine is fundamentally different under the covers... Just so you know.

15 Jan 2010

Hi Rob, nothing about anything not working in Silverlight shocks me anymore :)

Ollie Riches
Ollie Riches
15 Jan 2010

Love it!

Cameron MacFarland
Cameron MacFarland
15 Jan 2010

I really like this idea.

What I don't like is that using Expressions is slow, especially when you're initializing a couple of thousand ViewModels to put in a list of some sort.

Could you put the mapping in the static constructor, and pass in the type into the setup?

static EditCustomerModel()
{
    Property<Customer>(customer => customer.FirstName);
    Property<Customer>(customer => customer.LastName).Named("Surname");
    Property<Customer>("FullName", customer => string.Format("{0} {1}", customer.FirstName, customer.LastName));
}

public EditCustomerModel(Customer customer, CustomerRepository customerRepository)
{
    // Still need to define the command on the intance, but since it's not using reflection...
    Command("Save", () => customerRepository.Save(customer));
}

That way the reflection is done once, and cached, speeding up the whole process.

15 Jan 2010

Awesome - but a shame it doesn't work in Silverlight.

MicroModel would be useful in many cases and would compliment many MVVM designs/frameworks well. Having the AllProperties call + a Command would be enough VM in many cases. If MicroModel becomes - well - too Micro you could always write the more complicated models by hand, or just add tradition properties to the class deriving from MicroModel.

15 Jan 2010

Heck yes! I started to do something like this some time ago and was told that it wasn't possible. I had just started looking at TypeDescriptor when I decided to try something else instead. While I didn't write it, I feel vindicated that it is possible and even elegant! The comments about Silverlight remind me that was the reason I finally gave up.

15 Jan 2010

@Cameron - You don't need the static constructor for each micro-model implementation. A better solution might be a common, static cache in the MicroModel base class. Then, no matter how many times you assign it, you could look up the type and expression from a Dictionary and return the cached mapping. That would begin to approximate how the DLR caches its calls, I believe. Of course, we are now getting to the point where we should begin asking why we don't just use something like IronRuby or IronPython, which are supposed to be allowed to bind in Silverlight 4, iirc.

Cameron MacFarland
Cameron MacFarland
15 Jan 2010

@Ryan - That's what I was thinking of, caching the definition in a dictionary. I was just doing it in the static constructor to avoid threading issues. But the issue with that is how does the definition get the instance information. Currently Pauls solution is using closures to capture the instance, which means each definition is different for each model.

Anyway, it's the lambda being assigned to an Expression object that takes the time. If the lambda is assigned to a delegate it's fine. So you want to avoid doing lambda assignments to Expressions as much as possible. I've found them slower than straight reflection.

16 Jan 2010

I fully agree that a lot of MVVM "frameworks" put too much into the ViewModel.

A question about your MicroModels: As far as I understand, using MicroModels results in a direct coupling (through binding) from the view to the DOM (instance of Customer in your example) - meaning that everytime the user modifies data and data binding kicks in, the DOM is updated?

Or am I missing something?

Cheers Urs

21 Jan 2010

Hi all,

I was able to port this great library to Silveright. You can find more information here.

22 Jan 2010

Really nice!

I use Caliburn, so I changed my ViewModel base class to extend MicroModel instead of Presenter and then changed MicroModel source code to make MicroModelBase extend Presenter.

It worked for me. Now I have MicroModel and Caliburn working together.

05 Feb 2010

Paul,

I like this idea.

It seems everyone is trying to solve the same problems with object mapping/transform/binding.

What are your thoughts on bi-directional transforms? From quickly looking over your code my assumption is that the transforms for the non-simple properties is one-way?

It still feels like there must be a more elegant way to do this mapping/transformation. DSL with a visual tool? Compiler support? etc

It seems to me everything is pointing in the direction of a generic object mapping/transformation framework...

Yes there is AutoMapper but this is not "Live". There is WPF Binding but this is no good for POCO. There are various CLINQ style derivatives for mapping/transforming collections, but they are one-way by their nature.

Jack

Adrian Hara
Adrian Hara
23 Feb 2010

I'm probably missing something, but the Property method is protected, so the "Collection().Each((x,model) => model.Property...)" doesn't work.

Do I have the wrong bits?

04 Mar 2010

The Create method, in the PropertyReaderFactory:

public static IPropertyReader<TCast> Create<TCast>(Type objectType, string propertyName)

fails if the intended property is overrided. I fixed by doing:

//Try to find the property at the declared type level
var propertyInfo = objectType.GetProperty(
    propertyName,
    BindingFlags.Instance |
    BindingFlags.NonPublic |
    BindingFlags.Public |
    BindingFlags.DeclaredOnly
);

//If could not find at the declared type level, look in the base types
propertyInfo = propertyInfo ?? objectType.GetProperty(
    propertyName, 
    BindingFlags.Instance | 
    BindingFlags.NonPublic | 
    BindingFlags.Public
);

Regards,

Rafael Romão

Adrian Hara
Adrian Hara
07 Mar 2010

Well, I WAS missing something, Property() is both a method on the MicroModel class AND an extension method on the IMicroModel interface, and I wasn't importing the namespace of the ext. method. I only noticed today that the source code is available :) (weird?)

Rob Cecil
Rob Cecil
21 Apr 2010

Paul,

I'm trying to track down something - does MicroModels presume that the source objects (dto's, business objects, etc) that your Models reference are themselves observable things (i.e. they need to implement INotifyPropertyChanged)? Is there a way that the Models can be observable independent of the dto/data/business layer? Is there a difference in observability between explicit properties (lambda or otherwise), and implicit properties (AllProperties)? It seems that the AllProperties route always uses ReflectPropertyDescriptors as the bottom layer and preclude some kind of notification mechanism. I'm working in a project where the DTO's come from WCF and are simple datacontract objects.

Thanks

RobC

03 May 2010

Hey Paul,

Just came across this and it looks really cool! I'm actually working on something very similar, perhaps there is some crossover / collaboration we could do? Take a look at my library on codeplex and let me know what you think!

Cheers,

Robert

Dave
Dave
11 May 2010

I'm a bit concerned about the performance loss, yet the way you define objects is simply briliant. Perhaps a similar approach using a DSL will worth some researching; write the code generic using a custom language, and let the "framework" generate the specific code.

Jack Ukleja
Jack Ukleja
09 Aug 2010

The examples and library seem to favour a "model first" approach to MVVM, whereby the model is already loaded before the VM is constructed.

If that is the case how do you handle things like "loading..." screens?

xxxprod
xxxprod
28 Sep 2010

Hi Jack,

i had a similar question because how i've implemented my viewmodels they get their data after they've been constructed and adding the properties after the construction of the viewmodel results in an error because the modelBase is "sealed". Now I add the properties in the constructor with a dummy object and change the object later in the process and call "RaisePropertyChanged" for each property. For that I also added a new method to "Modelbase" which simply iterates all properties and calls automatically the RaisPropertyChanged method.

Her my example:

public class PersonViewModel : MicroModel
{
    private Person _person;

    int i = 1;
    public PersonViewModel()
    {
        Property(() => _person.FirstName);
        Property(() => _person.LastName);

        Command("ClickCommand", () => SetPerson(new Person { FirstName = "Max" + i++, LastName = "Mustermann" }));
    }

    internal void SetPerson(Person person)
    {
        _person = person;

        RaiseAllPropertiesChanged();
    }
}

And the new method in ModelBase looks like that:

public void RaiseAllPropertiesChanged()
{
    foreach (var property in _properties)
    {
        RaisePropertyChanged(property.Name);
    }
}

And by the way: Great tool you've created here Paul!

Jimbo
Jimbo
01 Oct 2010

Is MicroModels still available for download anywhere? Can't seem to find a valid link...

CikaPero
CikaPero
11 Oct 2010

Download link is broken! Please fix it so I can investigate source code of this interesting library. Thank you!

Stipo
Stipo
21 Nov 2010

For anyone interested, this project is hosted on Codeplex.

You can found it here.

16 Dec 2010

@Rob Cecil

If your business objects are not observable, perhaps you want to look at a hybrid solution with UpdateControls which appears to do what you're describing.