WPF MvvM - simple approach

By Mirek on (tags: mvvm, WPF, categories: code)

Model View View-Model is one of most popular design pattern used in WPF applications. Thanks to binding, dependency properties and other features, WPF has been adjusted and, I believe,  somehow designed to be used with MvvM pattern easily.

There are many MvvM frameworks for WPF which support creating loosely coupled and scalable wpf applications. We can use Prism, Cinch, MVVM Light and many more available on the network. We can also build our own framework if we do not need the whole stuff that comes with external framework.
In this post I am going to show you the approach for creating and managing loosely coupled views and their view-models backends. One of the ideas behind MvvM pattern is to make the application fully testable and scalable. This requires the model-view to be independent of concrete view and vice versa. To achieve that we can use WPF binding and commanding features. But how to create these objects and connect them so the binding and commanding works?
Let’s define following interface

   1: public interface IView
   2: {
   3:     object DataContext { get; set; }
   5:     void Show();
   7:     bool? ShowDialog();
   9:     void Close();
  10: }

and create the MainView.cs

   1: public partial class MainView : Window, IView
   2: {
   3:     public MainView()
   4:     {
   5:         InitializeComponent();
   6:     }
   7: }

All methods of IView interfaces are covered by implementation of Window class.

Now let’s see the View-Model for above view

   1: public class MainViewModel : ModelBase
   2: {
   3:     public MainViewModel()
   4:     {            
   5:     }
   6: }

The tricky part is contained in ModelBase class which will create the view for corresponding View-Model. Important hare is the naming of the views and corresponding view-models. By convention each corresponding view-model should has name as its corresponding view plus Model part at the end. So if we have MainView view then corresponding view-model must be named MainViewModel.
Let’s see how does ModelBase look

   1: public abstract class BaseModel
   2: {
   3:     public BaseModel()
   4:     {
   5:         View = IoContainer.ResolveNamed<IView>(getViewName());
   6:         if (View != null)
   7:         {
   8:             View.DataContext = this;
   9:         }
  10:     }
  12:     protected virtual string getViewName()
  13:     {
  14:         string name = this.GetType().Name;
  15:         return name.Remove(name.IndexOf("Model"));
  16:     }
  18:     public IView View
  19:     {
  20:         get;
  21:         protected set;
  22:     }
  23: }

In constructor we use getViewName() method to retrieve the view name from view-model name according to the convention. Then we use Inversion of Control container, I use Autofac, and try to resolve the view instance by registered name. Then in line 8 the view-model is assigned as DataContext of the view so the binding and commanding now can work.

Registering views is quite simple using Autofac, or any other IoC. We just search all types in assembly and if their name ends with View then we register it by name.

   1: void SetupDependencies(ContainerBuilder builder)
   2: {
   3:     //Register all views and viewmodels, find them by name
   4:     foreach (var type in Assembly.GetAssembly(typeof(App)).GetTypes())
   5:     {
   6:         if (type.GetInterfaces().Contains(typeof(IView)))
   7:         {
   8:             if (type.Name.EndsWith("View", StringComparison.Ordinal))
   9:             {
  10:                 builder.RegisterType(type).Named<IView>(type.Name);
  11:             }
  12:         }
  13:     }
  14: }

Having that all working we can easily open new window in our application but just creating view-model

   1: MainViewModel mainVM = new MainViewModel();
   2: mainVM.View.Show();

That’s it. Cheers