Fluent Silverlight – Part 1
Introduction
We (that is Ray Houston and myself) want to introduce a new framework we developed in the past few months. This framework provides a fluent interface to Microsoft Silverlight. As we have been able to leverage a lot of OSS software in the past we found that it is time to give something back to the community too. This was the birth of the OSS project we call “Fluent Silverlight”. The code is publicly available and is hosted by Google Code. It can be found here.
Motivation
This past year our company developed a Silverlight client for our .NET web application. The Silverlight code base has grown quite large and we’ve started trying to improve some of our biggest pain points. One of the biggest that we’ve faced, is our implementation Model-View-Presenter (MVP). We ended up with a lot of “ping-pong” communication between the view and the presenter.
We had a lot of code that:
- An event would happen on the view,
- be handled in the “code behind” of the view and tell the presenter,
- the presenter would make a decision and may tell the view to do something else
- possibily repeat the whole cycle.
We tried creating tests, but they only really tested interactions and didn’t test the true intent of the view. They only verified that “yes, we have an implementation” and did not test that the view was working as a whole. We didn’t feel as if we were getting a good return on investment with the MVP tests, so we ultimately stopped writing them
and just relied on integration tests to check the functionality.
As we began to trust data binding in Silverlight (we had been burned in the past and we did not have any prior WCF experience), we started to look at Model-View-View Model (MVVM) as an alternative. We really like the strongly-type declarative style of Fluent NHibernate and FuBu MVC, so we tried to see if we could do something similar with
Silverlight and MVVM.
Fluent Silverlight is an attempt to make MVVM easier by using a fluent interface with strongly-typed reflection to define controls and bindings and avoid the XAML based “wiring” of the view to the view model. It hides the implementation details so that they are allowed to change without changing the view or the view models they are bound to. An example would be the way it hides the ugly event handlers and allows you to associate controls events directly to commands on the model.
This will be a multi-part post series. In this first part we want to introduce some of the basic concepts of our new framework. Especially we want to show how a user control can be bound to a view model.
The Model-View-View Model Pattern
The model-view-view model pattern (MVVM) is often used when building WPF or Silverlight applications. We decided to use it as the primary pattern in our framework. The MVVM pattern is a variant of the MVP pattern. A good introduction into the MVVM can be found here.
The view model
The view binds to properties on a ViewModel, which, in turn, exposes data contained in model objects and other state specific to the view. The bindings between view and ViewModel are simple to construct because a ViewModel object is set as the DataContext of a view. If property values in the ViewModel change, those new values automatically propagate to the view via data binding.
The view model contains all the data a view needs for display. A view model in contradiction to a domain model can also expose display relevant properties. Examples of such properties are whether the content of e.g. UserName should be read only or not or whether a specific control displaying the content of a property should be visible or not in a certain context.
But the view model is not only a data container. Since it is a variant of a presenter it also contains behavior. It coordinates between the view, the (domain) model and the (backend-) services. As an important example it exposes commands that can be bound to an event of a control on the view.
Since data binding is used to update the controls on the view the view model must implement the INotifyPropertyChanged interface. Since every view model has to do this and since we wanted to keep or code DRY we decided to define a base view model class from which all other view models have to inherit.
Properties
We have been able to build an infrastructure such as that our ViewModel is very slick. Although we implement the INotifyPropertyChanged interface to make data binding possible we only need to implement each property as auto property in our ViewModel. Normally when implementing the said interface the code of a property looks similar to this
private string userName;
public string UserName
{
get { return userName; }
set
{
if(value == userName) return;
userName = value;
PropertyChanged("UserName");
}
}</p>
in our framework we have reduced this to
public string UserName { get; set; }</p>
which we think is a significant improvement. The technique we use to make this possible is interception. Basically we wrap the view model with a proxy. This technique will be described in details in an upcoming post of Ray.
Commands
To define a command in the ViewModel to which an event of a control can be bound we use so called DelegateCommands.
public DelegateCommand<object> SaveCommand;</p>
A delegate command usually is instantiated in the constructor of the view model and requires two methods delegates when instantiated. The first one is a delegate to the method that is called when the command is invoked through the user action. The second one is a delegate to a method which determines whether the command is available in the given context or not. The first delegate is mandatory whereas the second one is optional.
public SampleViewModel(...)
{
...
SaveCommand = new DelegateCommand<object>(OnSave, CanSave);
...
}</p>
By the way: the DelegateCommand has been introduced in the Microsoft Prism framework. We have slightly modified it to adapt it to our specific needs.
The controls bound to a command will be automatically enabled or disabled if the command is available or not.
The view model is an important cornerstone in our framework. We have provided it with many useful features such as that it merits it’s own post. The view model will be described in details in our next post.
Fluently bind existing XAML user controls to a view model
Imagine having loads of Silverlight user controls that are written in XAML. You want to leverage these controls without having to port them right away and use a fluent API to bind these controls to a view model.
Bind to a property
Lets assume that on the user control we have a TextBox called txtUsername. We want to bind the Text property of this TextBox to the property UserName of the view model. The syntax we would like to use to accomplish this binding is as follows
this.WithTextBox(txtUsername)
.Text(m => m.UserName)</p>
The binding is described with the aid of a lambda expression m => m.UserName in this case. I took the parameter m as it is an acronym for model. If you are not familiar with lambda expressions please refer to this post for a detailed introduction into delegates and lambda expressions.
The WithTextBox function is an extension method of the UserControl and takes as an argument the element which we want to bind to our view model.
But that’s not all. We want as well bind the IsReadOnly property of the same TextBox to the property UserNameIsReadOnly of the view model. Further more we want to bind the properties Visibility and Enabled to the corresponding model properties. Thus our code now should look similar to this
this.WithTextBox(txtUsername)
.Text(m => m.UserName)
.IsReadOnly(m => m.UserNameIsReadOnly)
.IsVisible(m => m.UserNameIsVisible)
.IsEnabled(m => m.UserNameIsEnabled)</p>
There is one important fact that I want to discuss here. Each FrameworkElement has a property Visibility which determines whether the corresponding control is visible or not. Unfortunately the Visibility is not a boolean but an enum although there exist only two possible states namely visible and collapsed. Since our view model should not contain any display specific logic we do not want to have properties of type Visibility in our view model but rather just use a boolean for properties concerning the visibility of a control. Fortunately Silverlight (as well as WPF of course) offer the possibility of so called value converters that one can use when binding a property of a control to a property of a model. We will make use of this possibility and write our own VisibilityToBoolean converter.
The above syntax look really nice and the code is fully type safe. If you have to refactor the view model and e.g. rename a bound property a tool like Resharper will automatically rename/refactor the above code for you too. Furthermore we can profit of full intellisense support.
Bind to a command
We do not only want to bind certain properties of our Silverlight controls to the view model but we also want to be able to bind events to specific commands defined in the view model. Most of the time this will be the Click-event of controls derived from the ButtonBase control such as a Button or a Hyperlink control. The syntax we want to use should be similar to this one
this.WithButton(btnCancel)
.OnClick(m => m.CancelCommand);</p>
Again the binding of the event to the command in the view model is expressed with the aid of a lambda expression.
As soon as a user clicks the cancel button the CancelCommand defined in the view model is executed. No need to implement any further code in the view! Our view stays perfectly humble as desired.
Furthermore a command can or cannot be executed in a given context. This fact is determined by some business logic. If a command is not executable at the moment then the Silverlight controls bound to this control are automatically disabled.
Fluently create and bind a user control
If we can start from scratch and create a new user control then we can do this by using the new fluent API. As an example we want to define a user control which contains a stack panel with some text blocks and some text boxes. Also there are two buttons save and cancel. In our user control (=view) we have an initialize method where we put the code to create the layout
public override void Initialize()
{
var grid = this.GridFor("Grid1")
.Background(Colors.Cyan)
.Margin(3, 10, 3, 5);
var stackPanel = this.StackPanelFor()
.Orientation.Vertical()
.Margin(4)
.AddChild(this.TextBlockFor("Book Title:"))
.AddChild(this.TextBoxFor(m => m.BookTitle))
.AddChild(this.TextBlockFor("Book Author:"))
.AddChild(this.TextBoxFor(m => m.Author)
.FontWeight.Bold()
.FontStyle.Italic())
.AddChild(this.TextBlockFor("Publisher Name:"))
.AddChild(this.TextBoxFor(m => m.PublisherName)
.ReadOnly())
.AddChild(this.TextBlockFor("Price:")
.FontWeight.ExtraBlack()
.Cursor.Hand())
.AddChild(this.TextBoxFor(m => m.Price)
.Width(80)
.HorizontalAlignment.Left())
.AddChild(this.StackPanelFor()
.Orientation.Horizontal()
.AddChild(this.Button("Save")
.Width(70)
.OnClick(m => m.SaveCommand))
.AddChild(this.Button("Cancel")
.Width(70)
.OnClick(m => m.CancelCommand)
)
);
grid.AddChild(stackPanel);
Content = grid;
}</p>
Usually this is the only code that is needed in a view. And as a result our view remains really humble. Please note as well that the only strings that are still present in the code are labels. Every thing else is strongly typed.
The above code does show some of the possibilities the framework offers. Layout elements as font styles, cursors, alignment or size can be set. Specific properties of controls can be bound to properties of the view model and the click event of button type controls can be bound to commands also defined in the view model.
We can even apply predefined templates to any control or a watermark to a text box as shown in the following sample
public override void Initialize()
{
plainText = this.TextBoxFor(m => m.TextAnswer)
.Interactable(m => m.TextAnswerInteractable)
.Visible(m => m.TextAnswerVisible)
.Template("textBoxTemplate")
.Apply(TextBoxStyles.GetStandardFontStyles())
.Watermark("Enter an answer please")
.VerticalAlignment.Top()
.HorizontalAlignment.Stretch();
Content = plainText;
}</p>
A watermark is a text that is displayed in a textbox if the corresponding textbox is empty and does not own the focus. Often the watermark text is displayed in a different font style and/or color that normal text (e.g. in gray).
Code
The project is called fluent-silverlight is hosted on Google Code. You can find it here. Feel free to browse and/or download the code and experiment with it. Any constructive feedback is highly appreciated.
Summary
In this post we have introduced a new OSS framework which provides an alternative way to implement a Silverlight application. The framework is built with TDD in mind and is meant to lead to scalable, maintainable and flexible applications. Views shall be as humble as possible since they are the only parts of the application that cannot be tested in isolation. All the presentation logic is implemented in the view model. Data and command binding is used between view and view model.
This framework is used in our internal application. We are still in a discovery phase. Many parts may change in the near future as we progress. Never the less we feel that it is worth to share our ideas with the community.
This post is the first part of a whole series of articles that will be published soon.