Mvpc – The View Layer in more Detail

This is the second post in the series of covering each layer of Mvpc.  This article covers the View Layer.  You can find the previous post on the Model Layer here.

View Layer Reponsiblities

Within Mvpc the view layer has very specific responsibilities.  In particular the view layer is responsible for:

  1. Displaying and formatting data from models to be shown to the end user.
  2. Interacting with the user through the GUI methods (excluding commands).
  3. Internationalisation and multi-language support where applicable.
  4. Making best use of the specialist hardware available on a device.

In design patterns that derive from the original MVC pattern (we’re not talking about the specific ASP.NET MVC libraries here but the design pattern itself) the view is often seen as the most GUI dependent layer.  This is true of the Mvpc design pattern too; but we have take special steps to take GUI and platform dependence down to a minimum while maximising the portability of any code you write.

Base Types for Views

When we designed the view layer we wanted to keep the class structure as flexible as possible.  As with the model layer we therefore use System.Object as the base class, rather than insisting on a specific base class type or interface implementation.

Making this decision allows the GUI portion of the view layer to use the base control types from the GUI toolkit being used, and with which you will already be familiar.  For example on the Windows Forms platform System.Windows.Forms.Control can be used as the base type.  On ASP.NET System.Mvc.Controller.  On Windows Store Windows.UI.Xaml.Controls.FrameworkElement, and so on.

Portable Views

We wouldn’t be able to write very portable code if we could only reference views within platform specific code; so under the toolkit specific views we have introduced the concept of a view based entirely on interfaces.

When creating views for a model by hand or using rapid prototyping each model usually has three view interfaces generated by default:

  1. ICreateView
  2. IEditView
  3. IListView

By convention these views exist in a namespace within the module matching the model’s name, but in plural form rather than singular.  For example if we hade a model class “My.Module.CRM.Models.Customer” then the full name of the three interfaces would be:

  1. My.Modules.CRM.Customers.ICreateView
  2. My.Modules.CRM.Customers.IEditView
  3. My.Modules.CRM.Customers.IListView

By convention we would also place all commands relating to a model under the same namespace with a .Commands appended to the namespace.  So in the same example we would use the namespace “My.Modules.CRM.Customers.Commands”.

This convention has been designed to keep all code relating to the business and application logic for a particular model within a single namespace.  Doing this keeps our code short and readable as the .NET compiler will search the current namespace, and then each parent namespace for a matching class or interface name when a short name is given.  So within a command we can simply reference IEditView rather than the full name, and we will be confident that the code will always reference the right IEditView.

An additional benefit this brings is what some refer to as static API inheritance.  That is to say that if the same code was lifted from one namespace, and placed into another namespace, most of the time the code would not need to be modified to refer to the new classes.  When learning the Mvpc libraries this represents a big advantage, as you get used to seeing the same class names over and over again and so quickly become familiar with their API and purpose even though you are technically dealing with different classes and interfaces.

As well as the conventional three views, the user can specify additional view by simply specifying a new interface.  By convention interfaces that represent portable views are prefiex with “I” and suffiexed with “View”, for example: IWebBrowserView or IMultipleEditView.

What is perhaps most interesting about the portable view layer is the definition of all the interfaces involved are left as empty interfaces.

Although you could specify specific methods or properties available in the interface, we strongly recommend that you don’t.

Its probably worth me explaining that decision before we continue.  When you write a view in a type safe language you will always want to write a view control to be as type safe as possible.  We recommend this too.  This means if you had a view that worked on a model of type Customer you would end up with code similar to this on the Windows Forms platform:

    [ViewFor(typeof(IEditView))]
    public partial class EditView : Control
    {
        public EditView()
        {
            InitializeComponent();
        }

        public Models.Customer Model { get; set; }

        private void EditView_SomeEvent(object sender, EventArgs e)
        {
            // We can directly access Model as a Models.Customer here due to type safety.
         }
     }

While this is good code and easy for the developer, if we put Model into the interface like so:

    public interface IEditView
    {
        public Models.Customer Model { get; set; }
    }

We are actually forcing all view implementations to follow this convention rather than leaving it as best practice.  Furthermore doing this would actually prevent somebody writing a view that can take a model of any type by accepting a System.Object for Model unless both the GUI view and the portable view specified it.  To me this is too nflexible and goes against the idea of keeping dependencies as few as possible within each code block and separating concerns.  As you will see later when we talk about the Standard Views included in the reference Mvpc libraries implementation, it would also prevent us from doing rapid prototyping effectively.

That’s why we keep our portable interfaces simple and empty instead:

    public interface IEditView
    {
    }

Creating View Instances

When working within a command or other code block we reference views by their portable view name.  So for example if I was in a command that handled an Edit request, I would want to use IEditView.  This is great for storing a reference to an instated class, but how do we create a new instance of the view, and how does it go on to create the actual GUI view we use?

The answer is that we use dependency injection to overcome these issues.  Specifically we have a specialised service locator called ViewFactory that we use to create all views:

var view = ViewFactory.Default.Resolve();

This code will create an instance of the actual GUI view implementation for IEditView and return it as an IEditView. Most of the time this will be right. However Resolve also comes in an extended form that allows a second generic type argument to be specified to resolve the type as another type. This is most often used to “downgrade” the return value into a System.Object, and is done by all the *CommandBase types included in the Mvpc reference libraries:

var view = ViewFactory.Default.ResolveAs();

By downgrading the code like this we are allowing a GUI view to be resolved for interfaces that it isn’t marked with. At first glance this may sound a silly thing to allow; but when you stop and think, its actually essential if we are to create general versions of views as we have within the StandardViews libraries, or to cope with models that are unknown at compile time, but generated at runtime using System.Reflection.Emit or a similar method.

GUI Views for each Platform

When creating the actual GUI for a view we finally put you into platform specific code.  Our recommendation is that when you write platform specific code you use the platform’s native GUI toolkit.  For Windows Phone this would be XAML, for Web it would be a Razor or traditional view file with controller. it would be the System.Windows.Forms namespace; and so on.

On each platform we do provide a set of useful controls in the Mvpc.GuiToolkit libraries.  However these controls are designed to complement the controls on the native platform, not to replace them and not as a cross platform GUI toolkit.

This decision stands out against the many cross-platform GUI toolkits that have come and gone.  As I covered in my introduction to Mvpc there are a lot of pros and cons with a cross-platform GUI toolkit, but by far the biggest con is that the application just doesn’t feel native alongside over applications running on the platform.  Insisting you use a cross-platform GUI toolkit would also prevent you using any existing 3rd party or open sources libraries for the platforms, all of which we believe would be forcing you to have to learn a new way of building GUIs, rather than maximising the skills you already have.

When designing the GUI for a platform we recommend that you also follow all conventions of the GUI toolkit you are using.  This means if best practice on your platform is to use a view model and the MVVM technique as with Windows Phone, do so.  If best practice is to store your GUI in resource files and load them from their, as with Android, do so.  If data binding is available for the platform as with XAML and Windows.Forms, then use it.

When designing the Mvpc design pattern we were very careful to keep compatibility with the MVC, MVP, and MVVM design patterns for the view layer.  This means that accept for decorating your GUI class with an attribute to link it to the portable view layer, you can ignore the fact you are even working with Mvpc if you want.  Even down to using core framework controls as simple views if you want.

The ViewFor attribute

Lets have a look at the attribute we need to use to link the GUI view to its portable interface; you saw it in the example before but we’ll take a closer look at it here:

    [ViewFor(typeof(IEditView))]
    public partial class EditView : EditViewBase, IEditView

You can see from this example that you simply need to mark a view with the ViewFor attribute specifying the interface type for the portable view it represents and the ViewFactory takes care of the rest for us. By convention we also make the class implement the (empty) portable view interface. This is considered best practice, but is optional for the reasons already discussed.

It is possible to mark a single GUI view as fulfilling multiple portable views. For example it is often sensible to share a single GUI view for both editing and creating new items as shown in this example code:

    [ViewFor(typeof(IEditView))]
    [ViewFor(typeof(ICreateView))]
    public partial class EditView : EditViewBase, IEditView, ICreateView

The ViewFor attribute is provided by the class ViewForAttribute and alongside the ViewFactory handles the dependency injection used by the view layer. It shares a common API with the attributes used to mark-up repositories, commands, and other dependencies. In addition to specifying the portable view type you can also specify a priority that is used if more than one class is found that implements a particular portable view type:

    [ViewFor(
        typeof(IEditView),
        Priority = 20
    )]
    public partial class EditView : EditViewBase, IEditView

When resolving views or any dependency the highest priority is the lowest number, i.e. 1 is considered a higher priority and therefore used over a priority of 300. If you don’t specify a priority the priority of 10,000 is automatically used.

By convention all the dependencies, including views, that are included in the reference Mvpc libraries have priorities close to Int32.Max. This means you can omit the Priorty from the attribute most of the time, and still know your class will be resolved with a higher priority than any included in the Mvpc libraries.

Before moving on from the attributes its worth making mention of an additional attribute for marking views only available on the ASP.NET platform.

By convention on the ASP.NET platform a Controller controls many Views with each method of a Controller representing an Action for GET or POST and having a related GUI view stored in a .cshtml or .aspx file.  To allow you to follow this recommended practice for the platform; when creating a ASP.NET GUI view we use an expanded attribute to allow you to specify the action within the controller for each supported portable view interface type, e.g.:

    [ViewControllerFor(
        typeof(IListView),
        Action = "Index"
        )]
    [ViewControllerFor(
        typeof(IEditView),
        Action = "Edit"
    )]
    [ViewControllerFor(
        typeof(ICreateView),
        Action = "Create"
    )]
    public class CustomerController : ViewController

The ViewHelper

I mentioned above the reason why we don’t specify a definition of a Model property in the interfaces that make up the portable view layer.  What I didn’t go on to do is expand on how we set the model in the view then.

Views actually have three optional properties that should be considered part of the portable view API when provided:

  1. Model
  2. Repository
  3. Commands

When working with portable views in a command or code block we will often want to set these properties.  We can do this using the static ViewHelper class.  This class gives us access to these properties for any object, and knows how to handle conversion of types to match the model type, and missing properties if the GUI doesn’t supply a property to store the value being set.

The methods come in Get/Set pairs and are respectively:

  1. GetModel() and SetModel()
  2. GetRepository() and SetRepository()
  3. GetCommands() and SetCommands()

We’ll cover off GetCommands() and SetCommands() in more detail when we look at the Command layer.  A real world example for using ViewHelper with the model and repository is in the code here:

var view = ViewFactory.Default.ResolveAs();
var repository = RepositoryFactory.Default.Resolve();

// Create the right model.
var model = repository.CreateUntyped();

// Set the model.
ViewHelper.SetRepository(view, repository);
ViewHelper.SetModel(view, model);

You can see from this example that usage is really straight forward and easy to understand and remember.

Standard Views

Now you know how to create custom GUIs for each platform, let me see if I can convince you to use your new knowledge sparingly.

Each time you create a custom GUI view for a platform, you are increasing the amount of platform specific code you will need to maintain.  If you had build a custom GUI view for all platforms this would start to spiral out of control, and those 90%-100% portable code rates would start to plummet.  There are certainly times to build custom platform specific views, but we want you to only do it when you have a special reason.  Otherwise we’ve tried to provide you with a rapidly prototyped GUI for every platform that should work for you in many cases.

These rapid prototype GUIs are found in the Mvpc.StandardViews libraries within the reference implementation.  In here there is an implementation of an Edit, Create, and List view for any model type specialised for all platforms.  There is also an IStartView for the initial screen and IWebBrowserView for commands that want to navigate or display HTML or other web based content.

These standard views are very flexible, and when combined with the GuiHintsAttribute and model metadata as described in the previous post in the series its possible to take a lot of control over how, where, and when these views show model data.  Its also worth pointing out that most of the same GUI hints are applied to your own custom made GUI views too.  We’ll do a special article on model metadata and GuiHints in the future.

We won’t go into the standard views in too much detail in this post, as it is already long.  I’ll also do a post on the standard views and how to get the most from them after this series is finished.  If you want to see the standard views in action however just put together a module without any specific GUI views and make sure you leave rapid prototyping turned on.  When you run the application all the GUIs that you see are using these standard views.  Check them on several platforms and you will see that they provide consistent access to data and functionality, yet follow the recommended GUI guidelines for each platform too.

Most applications end up a mixture of these standard views, and custom views.  This is a sensible approach, and consistent with the idea of maximising the amount of portable code compared to platform specific code that you write, while still providing a rich native experience.

In some situations you may be happy with the standard views for most platforms, but perhaps want to enhance on a specific platform to make use of a device specific feature such GPS location, scanner, or web camera for example.  In these cases you can provide custom GUI views for only the platforms you need, safe in the knowledge the standard views will work fine on the other platforms.

Conclusion

We went to great lengths with the Mvpc design pattern and reference libraries to re-think the way views and GUIs are built by developers compared to other design patterns.  Our primary focus has been to maximise the amount of fully portable code that can be written, while still allowing developers to use their existing skills, libraries, and best practice on each platform when they do choose to write custom GUIs.

You will have also started to see how a mixture of specialised and standard GUIs can be used to build applications that run natively and feel natively taking full advantage of a devices capabilities, while still keeping the vast majority of your code portable and shared across all platforms.

Next in the series we will take a look at the Presenter layer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: