Telerik Telerik
The Telerik Blogs

MicroModels for Silverlight

Wednesday, January 20, 2010 by Stefan Dobrev | Comments 11

Last week I was impressed by Paul Stovell’s marvelous project – MicroModels. In a nutshell a micro model is a view model over your objects, which allow you to define dynamic properties, collection and commands. You can read more about it in Paul’s introductory blog post here.

Despite its great features, “MicroModels” has one big limitation - it does not run in Silverlight. And this is where my journey began. My mission was to port the code to Silverlight with little to no modifications.

Porting the code to Silverlight

The task was not an easy one, but after a lot of head scratching and hair pulling I was able to complete it. It involved replacing the TypeDescriptor’s logic with dynamic type generation, partially evaluating local closures in compiler generated expression trees and other tricks typically developers don’t do in a normal working day. Despite all these hurdles, the good news is that the code is working fine in Silverlight and everything is supported.

Using MicroModels in Silverlight

The way the framework is used in Silverlight is almost the same as it is in WPF. You define your MicroModel with exactly the same way using your already familiar methods:

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));

    }

}

The only thing that you have to change is in the XAML. You will have to data bind your root DataContext to a new property of the MicroModel, instead of the model itself. The property is called Object. Here is the respective XAML for the EditCustomerModel:

<Grid DataContext="{Binding Object}">

    <Grid.RowDefinitions>

        <RowDefinition Height="Auto" />

        <RowDefinition />

    </Grid.RowDefinitions>

 

    <Button Content="Save" prism:Click.Command="{Binding Path=Save}"/>

 

    <Border Background="#f0f0f0" Grid.Row="1">

        <StackPanel>

            <StackPanel Orientation="Horizontal" Margin="1">

                <TextBlock Margin="1" Width="130">FirstName</TextBlock>

                <TextBox Margin="1" Width="50" Text="{Binding Path=FirstName, Mode=TwoWay}" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="1">

                <TextBlock Margin="1" Width="130">Surname</TextBlock>

                <TextBox Margin="1" Width="200" Text="{Binding Path=Surname, Mode=TwoWay}" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="1">

                <TextBlock Margin="1" Width="130">Full Name</TextBlock>

                <TextBlock Margin="1" Width="200" Height="50" Text="{Binding Path=FullName}" />

            </StackPanel>

        </StackPanel>

    </Border>

</Grid>

The same rule with binding to the Object applies to the Collection properties. Here is another XAML snippet, which illustrates the use of an ItemsControl bound to a collection property of the micro model:

<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=LineItems}">

    <ItemsControl.ItemTemplate>

        <DataTemplate>

            <Grid DataContext="{Binding Object}" >

                <Grid.ColumnDefinitions>

                    <ColumnDefinition Width="300" />

                    <ColumnDefinition Width="80" />

                    <ColumnDefinition Width="80" />

                    <ColumnDefinition Width="80" />

                </Grid.ColumnDefinitions>

 

                <TextBox Margin="1" Grid.Column="0" Text="{Binding Path=ProductName, Mode=TwoWay}" />

                <TextBlock Margin="3" Grid.Column="1" Text="{Binding Path=UnitPrice}" />

                <TextBox Margin="1" Grid.Column="2" Text="{Binding Path=Quantity, Mode=TwoWay}" />

                <TextBlock Margin="3" Grid.Column="3" Text="{Binding Path=LineTotal}" />

            </Grid>

        </DataTemplate>

    </ItemsControl.ItemTemplate>

</ItemsControl>

Samples

I have ported the sample project in the Paul’s source code so you can play with them as well. I have tried to reuse as much code as possible and almost everything except the XAML files is shared between WPF and Silverlight. Actually you can play with the example right here:

Downloads

You can download the binaries here.

You can download the source code from here.
You can browse the source code in my github repository here.

 

Have fun micro-modeling in Silverlight.

Posted in: Silverlight

11 Comments

  • Paul Stovell 20 Jan
    Really nice job Stefan. I knew the Silverlight port would be problematic because of the lack of TypeDescriptor support but it was so much easier than dynamic type generation. Very cool to see it come to Silverlight :)
  • Miro Paskov 21 Jan
    Brilliant! :)

    Partially evaluating local closures is a great idea, 10x for sharing.
  • Miguel 21 Jan
    Nice idea, I like the way you went around the lack of TypeDescriptors' limitation.

    There's only one small thing, after a quick lookthrough the code (I might be missing something) I think you're not reusing the types you create, which could be a performance hit and it might be an easy optimization. Some ideas
    * Based on type reuse it, e.g. come up with a naming convention instead of using the GUID try to use a type you already created.
    * Do the creationo on an static constructor and make your class MicroModelBase a MicroModelBase<T> where T: MicroModelBase. So essentially for each subtype you will call the static constructor and you create the "Object" there.


    Another benefit that I see of the type description approach (although I'm not even sure we even want to do that) is that we can add/remove properties at runtime.
  • Stefan Dobrev 21 Jan
    Hi Miguel,

    The dynamically generated types are cached based on the property descriptors they are created from. You can see that in the GetMicroModelObjectType(IEnumerable<PropertyDescriptor> properties) method here. It is using a TypeCache static field that holds already created types.

    Your idea about the static constructor seems interesting, but I'm not sure how it will apply in this scenario. We need to have a dynamically created type which will have the properties from the descriptors so that the Silverlight binding system can work correctly with them. Can you elaborate more on your idea?

    Thanks for the heads up.
  • kalvin 01 Feb
    Hi,
    nice work, but I have some strange situation. When I use AllProperties() for creating all property of object then everything is work OK. But when I use Property() binding dosen't work.
    It possible to connect standard binding from silverlight 3 with MicroModels? I allways get the same exception: "Exception has been thrown by the target of an invocation."
  • kalvin 01 Feb
    Hi,
    nice work, but I have some strange situation. When I use AllProperties() for creating all property of object then everything is work OK. But when I use Property() binding dosen't work.
    It possible to connect standard validation binding from silverlight 3 with MicroModels? I allways get the same exception: "Exception has been thrown by the target of an invocation."
  • Stefan Dobrev 06 Feb
    Hi kalvin,

    It should work for Property() method as well. Actually the samples are using exactly this.
    Have you checked the InnerException property of the exception you are getting? Have you set the ValidatesOnExceptions property of the binding?
  • Rob Cecil 20 Apr
    Stefan,

    I haven't checked Paul's original source drop carefully, because I'm working with Silverlight (by the way, great work getting this onto SL !)

    I'm trying to track down something - does MicroModels presume that the source objects (dto's, business objects, etc) that the 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 managed to get the unit tests for MicroModels running under SL without too much work, and that has been very helpful in understanding.

    Thanks

    Rob
  • Stefan Dobrev 27 Apr
    Hi Rob,

    If your source objects (dto, business objects) are not observable ones MicroModels will not be able to pick changes that you have made to the source objects themselves. However if you change the properties on the MicroModels those changes will be propagated back to the source objects. Also a MicroModel implements INotifyPropertyChanged interface so it is perfect for data-binding. You are right that for all properties a ReflectPropertyDescriptor is created, but it does not have the notion of property change tracking. It is happening in another place - look at the Dependencies folder.

    I was also able to port Paul's tests to Silverlight :). Unfortunately Paul and I did not have much time to spent on MicroModels project, because everyone is busy with his own stuff. We had some ideas about implementing IEditableObject and validation as aspects that can be injected into the view models, but this is still an idea. The last thing Paul have done is to move the source code of the project to CodePlex - here. If you would like to help us feel free to contact us through CodePlex system.

    Stefan
  • the Micro model of Silverlight Is very good tech. and That's more Shareable and I interested in tour post.
  • Andreas Larsen 28 Nov
    I really dig the idea of micro models, but I ended up not going with it due to two issues.
    1) With Resharper 6 you get static type checking in XAML, meaning it will tell you a view model property is spelled wrong and keep it in sync when refactoring. Really neat.
    2) It seems not possible to work with micro models in design time and Blend? Someone mentioned trying to subclass the micro model and fill it with mock data, but met a wall with some exceptions in Blend. I have not tried this myself though.
    My solution is now the regular mess of proxy properties and listening to model INPC events to propagate model changes to the view. PostSharp is one good looking alternative to keeping the INPC bloat out of the view model and model code, but I have yet to try it.

Add comment

  1. Formatting options
       
     
     
     
     
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)