65.9K
CodeProject is changing. Read more.
Home

Building an MVVM framework for both .NET and .NET CF

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

May 20, 2010

CPOL

2 min read

viewsIcon

9964

This post will explain the guts of the underlying architecture and some information about the design decisions used.

Introduction

As you might already know, I am maintaining a .NET CF application for one of my customers. Unfortunately, the design of the app is a total disaster, and maintaining it is actually a living hell. Fortunately however, my client decided to order a complete rewrite, which I started today. This post will not contain the source code (as it is a proprietary app), but rather explain the guts of the underlying architecture and some information about the design decisions used.

Maybe I will release some of the reusable components in the future under open source, but I am not quite sure yet...

Yet Another Framework?

Actually, yes!! My main concern about this app framework is keeping it lightweight, flexible, ultraportable and easy to use. By ultraportable, I mean that the app should both run on the default .NET, as well as on the Compact Framework, without recompilation.

Since no framework known to me meets my criteria, I decided to get started on yet another framework.

The main design decisions are heavily based on my previous experiences with MvcExtensions, my other framework, as well as a very inspiring and well-know lecture by Rob Eisenberg: "Build your own MVVM framework".

Service Tools

I have written and implemented the following interfaces which help me in developing the app.

A simple IOC container interface that looks like this:

public interface IIOC
{
  T Resolve<T>();
  T Resolve<T>(string name);
  object ResolveObject(string TypeName);
  object ResolveObject(string name, string TypeName);
}

And to register something into the container, I have the following methods:

  T Register<T>(T instance);
  T Register<T>(string name,T instance);
  T Register<T>(Func<IIOC,T> factory);
  T Register<T>(string name,Func<IIOC,T> factory);

Next to this, I also implemented a lightweight AutoMapper, which I use for just about everything:

public interface IMapper
{
  public void Map(object source, object destination);
}

And again, the implementation allows me to register some mappings using the following function:

void Register<TSource,TDestination>(Action<TSource,TDestination,IMapper> map);

Next to this, I also created a class called ActionResult (looks familiar, doesn't it ;) ).

{
  public object Viewmodel {get;set;}
  public ActionLink Redirect {get;set;}
  public bool Terminate {get;set;}
}

Although I have to say that I still have to implement the ActionLink class.

Gluing Everything Together

Everything you have read so far might seem awfully familiar to some people, which is a good thing IMHO. But having these tools available is still not the same as having an app framework, or is it?

Actually, it is almost; my main focus is in the convention-based Model-View-ViewModel approach, which allows me to have controller actions like this:

public class MainController
{

   IMapper sMap;
   IOrderService sOrder;
   IViewModelResolver sVMResolver

   public MainController(IMapper sMap, 
	IOrderService sOrder,IViewModelResolver sVMResolver)
   {
      this.sMap = sMap;
      this.sOrder = sOrder:
   }

   public ActionResult Index()
   {
      var vm = sVMResolver.Resolve<VMIndex>();
      sMap.Map(sOrder.GetOrders(),vm);
      return new ActionResult() { Viewmodel=vm };    
   }
}

Which should also look very familiar!!!

.... In A Single Form

Now, we have an ActionResult which contains a model, how do we know which form to show??? It is quite simple: I use a convention. All my Viewmodels start with the letters "VM", and all my views (WinForms usercontrols) end with the word "View".

I use a veiwmodel-first approach, i.e., based on my viewmodel, I choose which view to show. This looks like the best approach to me...

I have one Form named MainForm, which contains the following property:

object _Viewmodel;

public object Viewmodel
{
  get
  {
     return _Viewmodel;
  }
  set
  {
    if (_Viewmodel == value)
      return;
     _Viewmodel = value;     
    var t = this.Namespace+"."+_Viewmodel.GetType().Name.Substring(3)+"View";
    var vw = IOC.ResolveObject(t) as UserControl;  
    this.SuspendLayout();
    this.Controls.Clear();
    sMapper.Map(_Viewmodel,vw);
    this.Controls.Add(vw);
    foreach (var prop in _Viewmodel.GetType().GetProperties())
    {
       var ctrl = vw.Controls.Where(x=>x.Name==prop.Name).FirstOrDefault();
       if (ctrl==null) continue;
       sMapper.Map(prop.GetValue(_Viewmodel,null),ctrl));
    }
    foreach (var meth in _Viewmodel.GetType().GetMethods().Where
	(x=>x.Name.StartsWith("On"))
    {
        // do something similar for the methods; 
        // bind them to the corresponding actionlinks
    }
    this.ResumeLayout();
  }
}

And then my main loop will look something like this (not implemented yet):

void Run()
{
    var frm = new MainForm();
    var ar = new ActionResult() {Redirect=new Redirect<MainController>(x=>x.Index())};
    while (!ar.Terminate)
    {
       if (ar.Redirect!=null)
         ar = InvokeActionLink(ar.Redirect);
      else    
      {
         frm.Model = ar.Model;
         ar = WaitForActionResult(frm);
      }
    }
}

Conclusion

While the framework is not yet complete, this is the way I am going to implement the whole thing... If you have any suggestions or comments, please do let me know what you think !!!

Bookmark and Share