Introducing MicroModels

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

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.