ViewModel Pattern extended with the Dynamic Language Runtime

More fun with the ViewModel or M-V-VM pattern - using the Dynamic Language Runtime in Silverlight to create a better glue between the View and its ViewModel.

In my last post, I posted an implementation of the ViewModel or M-V-VM pattern for use in Silverlight applications. This pattern allows you to decouple your view presentation logic and data from the view, thereby facilitating independent development/design of your app as well as easier unit testing of your application code. I will highly recommend you check out that post for the more detailed description and intro sample if you haven't already done so, as this post will build further on that.

Now that I've done some additional exploration, and prototyping, I think the syntax used to define the glue between the View and the ViewModel as first presented was a bit suboptimal, and could be re-done to be much more intuitive. Read on to find out more.

Amazon Search Sample
In that post, I had a simple Amazon Search app to demonstrate the view model pattern. Focusing on the search aspect of the scenario, here are the relevant snippets of code and XAML forming the view model and the view respectively:

public class SearchViewModel : Model {
    ...

    public void Search(string keyword) {
    ...
    }
}

<vm:View xmlns="..." xmlns:x="..."
  xmlns:vm="clr-namespace:Silverlight.FX.ViewModel;assembly=Silverlight.FX"
  xmlns:app="clr-namespace:AmazonSearch.Views">
  <vm:View.Model>
    <app:SearchViewModel />
  </vm:View.Model>

  ...

  <Button Content="Search" IsEnabled="{Binding CanSearch}">
    <vm:ButtonEvents.Click>
      <vm:InvokeMethod MethodName="Search">
        <vm:ElementParameter ElementName="searchTextBox" ElementProperty="Text" />
      </vm:InvokeMethod>
    </vm:ButtonEvents.Click>
  </Button>

  ...
</vm:View>

Action behaviors were used to implement the glue that allows a View to invoke some operation defined on the View Model in response to an event. As you can see in the example above, an InvokeMethod action has been associated with the Button's Click event and it is set up to invoke the Search method on the ViewModel passing in the Text in the specified TextBox.

InvokeMethod Shortcomings in Associating View to ViewModel
The first issue with this approach is that it isn't super-friendly. The XAML to define a list of parameters gets verbose very quickly. Secondly it doesn't allow for a number of scenarios, around passing in arbitrary data into the view model. I noted in the post that I might want to do something like this instead:

<Button Content="Search" Click="{Action InvokeMethod Search(searchTextBox.Text)}" IsEnabled="{Binding CanSearch}" />

I got to thinking about this after writing and re-reading the post myself, and what occurred to me is that this is quite a slippery slope, eventually leading to a full expression language. Not wanting to create such a thing, I started reflecting on what could be repurposed.

I realized the Dynamic Language Runtime (DLR) would be a perfect fit. It is just the right designer-friendly glue that can be used to call into the bulk of the logic that is itself developed using statically compiled code that exists in the view model and the rest of the app. I could scope the use of the script down to a minimum by hosting the DLR to execute just script expressions. The DLR would also give me the benefit of using something like JavaScript, and potentially on any another scripting engine created to run on the DLR.

DLR-based InvokeScript
So I got to work, and created an InvokeScript action (leveraging the extensibility of actions), and got this working:

<Button Content="Search" IsEnabled="{Binding CanSearch}">
  <vm:ButtonEvents.Click>
    <vm:InvokeScript Script="$model.Search(searchTextBox.Text)" />
  </vm:ButtonEvents.Click>
</Button>

The verbose XAML for defining parameters was gone, and I didn't have to create my own mini-language. However this is still too much XAML for me. With a bit more work, I was able to get the following syntax to work:

<Button Content="Search" vm:ButtonEvents.Click="$model.Search(searchTextBox.Text)" />

The InvokeScript does its work by hosting the DLR. As a DLR host the InvokeScript action is able to resolve things like $model to the current ViewModel instance. It also resolves "searchTextBox" when the script engine hits that. To do so, it first looks to see if it's a property of the Button (which it isn't, so it continues to search). Next it looks to see if it can find it in the visual tree using FindName or in the resources dictionary via a recursive FindResource (and it does find it in the tree). The bottom line is the script expression is simple and intuitive. And the DLR host does all the work through an intuitive set of rules, rather than have those be spelt out in a verbose form in XAML.This has me pretty satisfied.

As an aside, the script expression can also reference a variable named $event that represents the current event. This is useful if there is some interesting data in the event argument that you want to send across to the ViewModel operation (eg. mouse coordinates in a mouse event) or some property on the event argument you want to set by using the return value of the operation itself (eg. Canceled on a CancelEventArgs).

Essentially the InvokeScript action creates a little data flow pipe as shown below that flows event information and pieces of data from the View into the operation in the ViewModel. The pipe is also able to flow back the return value from the operation for use in the View, even though it hasn't been depicted here.

You might be wondering how the script engine is instantiated. This gives me a chance to introduce my framework's derived Application type that is used in App.xaml - XApplication. Amongst various other pieces of functionality, XApplication provides the semantics of being a global service container. The application developer can write this markup in App.xaml to declaratively define the instance of a ScriptService that will be used in their application. At runtime, the InvokeScript action looks for the current XApplication instance, and queries for the ScriptService service and uses the ScriptEngine that the service is responsible for instantiating and managing based on the specified language.

<fxa:XApplication xmlns="..."xmlns:x="..."
  xmlns:fxa="clr-namespace:Silverlight.FX.Applications;assembly=Silverlight.FX">

  <fxa:XApplication.Services>
    <fxa:ScriptService ScriptLanguage="JavaScript" />
  </fxa:XApplication.Services >

</fxa:XApplication>

Note that you'll need to package the DLR assemblies (Microsoft.Scripting.*.dll) and the language specific ones (such as Microsoft.Jscript.*.dll for JavaScript) into your application's xap package. These are part of the Silverlight 2 SDK. Just add a reference to those dlls in your application project in Visual Studio and the XAP packager will do the right thing to include them in as part of the build process.

Code and Summary
As usual you can download the all of the code, sample, and framework to play with this to your heart's content. Do you think the use of DLR here helps signficantly simplify the view model pattern?

Posted on Friday, 6/20/2008 @ 8:34 AM | #Silverlight


Comments

25 comments have been posted.

Steve

Posted on 6/20/2008 @ 12:54 PM
By the way, your blog crashes my FF3, I had to use IE to view this blog...

MichaelD!

Posted on 6/20/2008 @ 1:17 PM
This is definitely sexier. The only question I have is how the ActionTypeConverter is used within Silverlight.FX. I did a quick Reflector and it doesn't appear to be used anywhere, but it does look like it's used to convert a string to an InvokeScript object. Must be some voodoo black automagic going on somewhere, perhaps?

Anyways, great step. Can't wait to see this on CodePlex. ;)

matt

Posted on 6/20/2008 @ 1:34 PM
@Steve : It crashed for me too until I uninstalled Silverlight 2 beta1 and installed beta 2.

Nikhil Kothari

Posted on 6/20/2008 @ 1:59 PM
@Steve, @Matt - Yes, you'll need the latest beta of Silverlight, which works around FF3 breaking changes - there has been quite a bit blogged about it, so I won't go into the nitty gritty details right here.

@Michael -
I'll be thinking about whats the best way to take this forward in terms of CodePlex - depends on how much of this prototyping is going to end up in a product. I might at the very least set up a page on my projects site, just to track it outside the context of blog posts.

On the TypeConverter and associated voodoo - the magic that is happening is the XAML parser using the type converter to turn a string into an Action. It detects a property of type Action but sees a string in XAML, and so it checks if there is a TypeConverter either defined on the property or property type to convert from string to specific type. Hope that helps explain how an InvokeScript action is materialized from the script expression.

On a related note, feel free to download the project - it includes sources for the framework, so you don't have to reflector-around, and further you can build, put breakpoints etc. to really see the details of what is going on.

casper

Posted on 6/26/2008 @ 3:37 AM
Does anyone else get crashes in the LINQ statement for parsing the XML returned from Amazon? It doesn't seem to matter what I search for...

Nikhil Kothari

Posted on 6/26/2008 @ 8:16 AM
@Casper -
It is probably the case that the search you are issuing returns xml that doesn't contain one or more elements in the returned markup that I am assuming are there.
I wrote some simple XLINQ code, but if the amazon search results vary in terms of xml structure, and the fields I am not looking are always there, that piece of code will need to start conditionally pulling out the bits of info it cares about.

Chris

Posted on 6/30/2008 @ 12:32 AM
In this pattern one idea is that the view has as few logic as possible, also the required knowledge of using methods should be minimized to ensure all implemented views work the same.

In this case I would recommend that the search method doesn't have a parameter. Instead the view should have a property called SearchTerm which is fetched from the ViewModel when Search() is called.

Nikhil Kothari

Posted on 6/30/2008 @ 8:38 PM
Chris, that is certainly an approach to take (it is in some sense a choice on the part of the developer/designer pair). However I personally disagree that all methods on view models should be parameterless.

Inventing artificial properties on the view model increase the testing matrix on the view model end. Having those properties also don't imply what is required for what method... that becomes guess work for the designer developing the view, who doesn't and shouldn't have to study the code for the methods to build that knowledge.

I totally agree that the view should not take on control flow logic and other interesting logic that needs to be tested independently of the view.

casper

Posted on 7/1/2008 @ 1:14 AM
In case anyone's curious, the Amazon query doesn't always return images. The simplest fix is to modify the XLINQ to this:

ImageUrl = item.Element("MediumImage") == null ? "" : item.Element("MediumImage").Element("URL").Value,

Chris Swain

Posted on 7/1/2008 @ 8:10 AM
This is the beginnings of a nice set of helpers. I can't wait to see the final product!

Miguel Madero

Posted on 7/5/2008 @ 3:21 PM
Nikhil,

Obviously this solution is better, but the references to *.Scripting*.dll increase considerably the size of the package. Do you know of any way of reducing or at least reusing those dlls among packages?
Do you think those would be included later as part of the SL plugin?

Nikhil Kothari

Posted on 7/5/2008 @ 6:10 PM
Yes, that is indeed the one downside... there are ways to share dlls amongst multiple xaps, however that sort of sharing lends itself better to splitting out features of an app, rather than parts of a single view (because you have to download any dependencies before loading the view). Still worth thinking though - I can see how creating additional app level patterns could facilitate that.

I don't think these will be part of the core in SL2. I certainly wish the DLR core was part of the platform, even if not specific languages.

Finally, I also have thoughts around the possibility of including a very mini-language parser that doesn't require all of the DLR - just enough to support method calls with simple sequence of parameters, identifiers, and property lookup. Nothing more, given a view is supposed to have minimal logic. Any opinions or thoughts on that?

Miguel Madero

Posted on 7/6/2008 @ 1:47 PM
I understand what you're saying, but doesnt other depencies are cached by the browser? That way the DLR dlls and other app level dlls would be loaded only the first time and other views will be loaded faster because all their depencies would have been downloaded.

About the minilanguage you mention, I tought of another option already in SL and it's a different approach to a minilanguage. The way converters work in Binding, they basically pass a parameter to a know method of an specified class. Here we already have a reference to the model, we only would need to specify the method and pass the parameters. I'm not sure how expressions like Binding, Static Resource or Converters are defined inside attributes and how one could do something similar for a 'MethodEvaluator', but this could be another option, probably the harder part is to get the EvaluatorParameter.

vm:ButtonEvents.Click="{MethodEvaluator Object='model', Method='doSomething' EvaluatorParameter={A Complex expression or simple param, probably a binding or something}}"

It's probably uglier than the first approach, but just an idea....

Nikhil Kothari

Posted on 7/6/2008 @ 8:47 PM
That is sort of where the {Action} markup extension started. One problem is currently custom markup extensions cannot be built. The other thing is subjective - for my taste, the nested markup extension approach yields too complex looking attributes. I generally prefer something intuitive and readable...

Miguel Madero

Posted on 7/7/2008 @ 11:29 PM
I aggree... probably the mini-language could be an option, I rather write my code in C# or Ruby and would be nice to have more options using the DLR, but it has this draw back, any way, it's supposed to have really simple code, so a mini-language might be enough, easier to use than markup extensions and lighter than using the DLR.

Signed/Shared dlls and ondemmand distribution/installation seems like a needed feature for Silverlight for this kind of escenarios, something like a SL Client GAC. For example, if my app requires the DLR, it will look first to see if it has the necesarry dlls, if it doesnt it could get them from my site and save them (not in the browser cache, but in a GAC), then if app2 requires the same version of the same dlls it could use the ones provided by my site. So it will be a similar experience to SL installation, first time it might take a while, but once it's installed you only need to load the application specific assemblies. Something similar could be done with other assemblies like the framework you did, EnterpriseLibrary and CAB for Silverlight or third party controls. Licensing would be another issue.
I know for v1 of SL CLR they will try to be conservative in features that could represent a security risk

Nikhil Kothari

Posted on 7/8/2008 @ 7:58 AM
Miguel, I was actually involved in a number of early discussions around creating this sort of extension cache fairly early on.

In addition to security ramifications, various other things are interesting - cleaning up, defining the identity of the assemblies (URI is not enough, maybe strong name, the trick is knowing the strong name before downloading from the URI, by mapping one to the other), how much to make this a general feature vs. a framework-scoped thing (that is a bit simpler, and has highest return in value), application load process (right now the app startup sequence is relatively clean - the xap has everything needed to bootstrap the app) etc.

All that said, while this is not going to happen in SL2, it is high amongst the set of things we plan on taking another look at for a future version.

Richie Scott

Posted on 7/8/2008 @ 2:55 PM
Nikhil,

Have just read this and your previous post regarding M-V-VM and all I can say is 'THANK YOU'. Its great to read a blog that isn't engrossed in how to display a button at a 90 degree angle. After reading this particular blog the benefits of DLR (i.e. simplification of code) seem obvious - my only problem is I don't have enough knowledge of DLRs to sufficiently comment on their real usefulness. Can you suggest any good DLR resources / blogs / articles, e.g. Learn DLR in 24 hours for Dummies :) ?

Thanks
Richie

Nikhil Kothari

Posted on 7/8/2008 @ 6:16 PM
@Richie
The best resource (or at least starting point) for DLR stuff is probably http://silverlight.net/learn/dynamiclanguages.aspx.

Beyond that the specific syntax basically is language specific. DLR for Silverlight I believe comes with support for JavaScript (which I am familiar with), Ruby (in the form of IronRuby) and Python (in the form of IronPython) - need to actually spend some time with the last two to learn a bit myself!

I totally agree, there are a lot of interesting topics around application/data architecture and patterns beyond UI topics, but the UI stuff is still quite important overall to a RIA app. So while I probably won't get into rotating buttons 90 degrees, I am also looking at UI stuff at the same time - in fact I blogged more recently about theming, and I am hoping to blog soon about some subtle visual effects. :-)

Richie Scott

Posted on 7/9/2008 @ 1:23 AM
Nikhil,
Totally agree about your UI comment (in fact I have just finished reading your theme article), I was more making the point that a lot of blogs / articles tend towards the 'how to' deliver the WOW factor, or as I like to call it 'the how to impress your manager factor'. The framework / architecture / best practices of Silverlight programming to me are a lot more important and a lot more worthwhile, thus the reason I like articles like this :)

Miguel Madero

Posted on 7/9/2008 @ 8:38 PM
Nikhil,

Most of the topics will certainly be resolved in long meetings, I know, there are a lot of things to consider beside what we could think in a couple of posts, but just to expand on your points:

Cleaning up, this could be left to the browser cache, finally is another downloaded file, for XAP files, SL CLR could simply decompress them and cache the separate assemblies.

Defining the identiy, we would definately use the SN. The process could be something like, download the XAP, read the manifest, look for the entry point, start executing and as needed load depencies (or preload/predownload depencies assuming they might be used). The order would be from the XAP, from assemblies recently downloaded from that site, from the IE GAC (just to give it a name), then try to download it if not avaiable. At every step of the local search, based on the depency the original assembly has it will look for an assembly using the PublicKetToken, if it doesnt have one it wont look in the IE GAC and simply go to the next step (download it from the same folder the xap was downloaded from). For the initial steps the depencies could be resolved similart to the way they're in the desktop, the final step is the only problematic one (the download part is already done) and we have to options, send the PublickKeyToken, version etc, through the query string or form params, but this would need some work on the server and SL is a Cliente Technology and dont want to depend on a special handler for IIS (and/or other WebServers) so the other option is to simply download the file assuming than if it's there it might be right and then check it once its downloaded.
So URI it doesnt really matter if the first time it was downloaded from microsoft.com and then nikhil.net needs it and later miguelmadero.com needs a newer version and later nikhil.net request the newer one, as the assembly has an SN, nikhil.net could be sure it has the needed assembly no matter where it was downloaded from.

This definately should be a general feature, but if you do it know for an FX it could be integrated later in SL 3 :)

Application load process would be less, because as part of that process is the download process and if the XAP file could be smaller the app load time will decrease and that will probably be the case for bigger sites with shared components.

Jonas Follesø

Posted on 7/16/2008 @ 10:40 PM
Hi Nikhil,

Elegant solution, but one of the problems with this solution is that it doesn't work in Blend... Part of the promise of patterns like MVVM is to enable designers and developers to work together...

I guess this could change as Blend and Silverlight mature towards RTM, but for now I'm looking into a more "traditional" Command pattern implementation found in the "Silverlight Extensions" package: http://www.codeplex.com/SLExtensions

You hook a command to a button like this:

<Button Content="Open" Width="50" Height="30" sle:CommandService.Command="Open" sle:CommandService.CommandParameter="{Binding Path=OpenFileInfo, Source={StaticResource controller}}" />

The problem with this solution is that it "infers" the correct event to hook (basically if it's a button it will hook the click, if it's a textbox it hooks keydown, and for everything else it hooks the mouseleftbuttondown event...

Cheers,
Jonas

Nikhil Kothari

Posted on 7/17/2008 @ 9:33 AM
The command pattern tends to work well for Button controls, and methods with zero or one parameters, as far as I can tell.

In my opinion, it isn't extensible/flexible enough (arbitrary controls, events, logical events, methods with more params, methods on arbitrary other view components that don't surface commands etc.) and requires the dev to create and surface commands (from a System.Windows.Input ns in their code that needs to be decoupled from presentation) rather than simply authoring some methods.

Needless to say, tooling is important... and I am in fact in the middle of a set of meetings with the tooling folks on this area.

KierenH

Posted on 9/10/2008 @ 9:57 PM
This is a cool idea, but don't really want to write code inline in Xaml (origins of codebehind, aspx, aspx.cs)?

Would be nice if a designer had the ability to hook up the View to the ViewModel though. Commanding is nice in this regard.
The designer chooses the Command by name, and hopefully the developer has implemented the handler for the command (in code behind).

KierenH

Posted on 9/13/2008 @ 6:55 AM
Another thought...

How do we represent interaction between controls that don't have a representation in the model. In Wpf, controls can be bound to each other, how could we do this declaratively in our Xaml code.

Consider this scenario:
The designer wants to make Content of the View scalable using a Slider control.

Case 1 - Simple
In this case, there is no glue between controls in Xaml.
There exists a Zoombox control with a [TemplatePart( Name=Slider, Type=typeof(Slider))]
Pros
-The designer can create the view and the Slider will automatically be hooked up
Cons
-The Slider has a strong association with the Zoombox, so UI customization is limited
-The developer had to write the repetitive plumbing code to hook up the Slider (in the Zoombox control)

Case 2 - Not So Simple
In your Amazon search sample, you invoked a method on the model which then updated properties which were bound in the view (IsSearching, Products).
With a Zoombox, my action to invoke is 'Zoom In'. But it doesn't make sense to provide zoom oriented properties on the model (atleast to me).

How do I invoke ZoomIn on the Zoombox, and update the Slider, using a declarative approach that is repeatable.

mirc

Posted on 9/26/2008 @ 6:25 AM
thanks.
Post your comment and continue the discussion.
 
 
 

 

 
Refresh this form if the spam-protection code is not readable, or has expired. (Your input will be preserved)