Rico is a fairly ambitious programmer, and he chose to do this in WPF a few months ago. I was just assigned to help him a couple of weeks ago. The project was already in full-flight at that time. I personally would have preferred to do the project with ASP.NET MVC framework. However, my firm still expresses a preference for winclient apps.
I am no stranger to forms over database tables. I did nothing but this kind of application for some 10 years of my life. It was only somewhere around 2004 that I began to work heavily with web apps. If you have not yet tried WPF and the Model-View-ViewModel approach to application achitecture, let me be the first to tell you: You are in for a quite a shock. The world has changed, and it hardly resembles the old paradigm you remember. These are not your daddy's VB or Delphi or PowerBuilder forms.
Before I get into my personal reaction to this architecture, I want to try to give you a vision of the end-game. You have to understand the absolute goal and the final ideal driving these monumental changes before you can begin to balance what I am about to say.
The absolute final objective of Model-View-ViewModel WPF is to create an application in which the interface design is absolutely sepperate from the application logic. In this way, we will achieve an application that is totally skinable. This idea is beyond the simpler notion that a graphic designer should do the interface with Adobe Illustrator and crank out XAML for inclusion in the main project. A WPF application, done correctly according to this pattern, should be able to radically change its look just as the Opera or FireFox web browser can change their skins. We should be able to reach the point where:
- An application skin can be developed totally sepperately from the main application project
- An application skin, compiled into its own distinct assembly DLL or EXE, can be downloaded by the end user and applied to his/her installation of the app.
- The end user can chose between one of several skins dynamically at runtime.
- If the skin is housed in a DLL, you should be able to change skins without a restart. If the skin is housed in an EXE, a restart will be required.
We should be able to go well beyond that which Opera has done. We should be able to do what the great developers of Nero have done. Users familiar with the various apps in that suite will know what I am talking about.
What it takes to get there is nothing short of shocking. VB/C# programmers used to the automatic and trivial nature of WinForm data apps are not going to be pleased. In fact, you will be deeply dismayed.
- Code behind the XAML form is forbidden.
- The implementation of the interface must be completely delegated to the XAML coder.
- References to the View from the ViewModel are forbidden. The C# or VB coder who writes the ViewModel can do nothing to create or manipulate WPF control objects on the XAML window
- XAML code should make binding references ViewModel class properties only
- The ViewModel can make no references to the XAML window.
- You need a pure data model, which usually consists of Entity Framework classes.
- You need one ViewModel per XAML form, which is designed to collect and present model classes in one or more ObservableCollection for use by a XAML form (any XAML form)
- You need to do a shell EXE which has just one responsibility: Create an inversion of control container that maps Views to ViewModels according to matched pairs listed in something like an app.config.
So, two questions might pop up immediately:
- Doesn't an Inversion of Control Container (IoC) map abstract types (interfaces or abstract classes) to specific concrete types?
- Why wouldn't you a real app.config?
In short the answers are: Yes but not precisely in this case; and because you want to write data.
In our specific case, the ViewModel knows and understands that there will be some sort of XAML based form that will come along and make use of it. It does not know the name of that class or anything specific about that class. In sooth, it cannot. If it does, we get some form of coupling which makes it more difficult to rip off the entire interface, and use another one. Any reference to any aspect of the view makes it more difficult to download a new interface and slap it on. Everything will be hindered. The ViewModel does not keep track of even an abstract interface vis-a-vis its XAML form. The ViewModel needs full ignorance of the View, almost in the same way that ORM guys want the model to feature full persistance ignorance.
Rather, the EXE needs a simple string key naming the View XAML. This key word will be passed to the IoC. The IoC will resolve the class for the key, and it will create and return an instance of this XAML form from it's containing assembly.
The consequence is that you wind up with five layers of code in your app:
- A model containing Entity Framework classes. Each entity represents one table.
- A ViewModel containing groups of entities necessary to support a specific CRUD form, whose name and design will be spelled out later. These groups of entities need to be grouped in ObservableCollections
- A View that is written in XAML and uses binding to spell out how it uses the ViewModel
- A shell EXE that contains an IoC which decides which XAML form should be invoked per ViewModel at runtime.
- The shell EXE will read a config file of some sort (YAML, BAML, XML, XAML, INI etc.) which spells out matched pairs of ViewModels to Views. It will invoke the correct XAML form per ViewModel, as per your config file settings.
- Be happy. You now have a skinnable app.
I am afraid it is even more complex than it sounds. The ViewModel needs to implement some important Interfaces, and the code is quite redundant. It is best to lock those implementations in an abstract base class which each ViewModel must inherit from. Call it ViewModelBase, or ViewModelAbstract. Even so, you will need to implement a property, a "Can__" Delegate for each important event around that property. To implement one bindable table in a ViewModel, you may need a private field, a public property, several events, and a flag or two.
This is presuming you want a vanilla data form, and nothing flashy. Flashy will cost you more code. If you have something special in mind, you will need to figure out how many delegates and properties you need to make that happen.
The net effect is that you will write 5 to 10 lines of code where you would have written 1 or 3 lines of code before. This is usually felt as a drop in productivity and an increase in worktimes.
Are you starting to get the picture? CRUD forms ain't trivial anymore. Once learned the code is quite rote. It can be done by boilerplate. This is highly subject to automation through code generation. Rico has already developed a rudimentary ViewModel generator (in WPF no less) and I just helped him to extend it on Friday. We don't intend to write this code by hand.
This naturally begs the question: If this is highly redundant code, totally subject to generation, why didn't Microsoft just lock this stuff up inside the framework as they always do? If this is completely patterned work, which not hide the wiring under the hood? The answer is surprisingly simple: Because Microsoft has gotten sandbagged over the years for making large numbers of highly-restricting architectural decisions for developers. Developers have demanded full control. They have demanded that Microsoft stop hiding the wiring under the dashboard. Let us write the Dashboard. We are tired of seeing demos for easy frameworks, wanting to customize the easy framework, and then discovering that it is totally non-customizable.
Well... some programmers have demanded this at least. We should strain to remember that as of now, WPF is a market failure. Very few apps have been written with this framework, and few corps have adopted it. The Model-View-ViewModel framework is hardly out of baby-diapers. Fewer still have chosen to use it. Most rudimentary programmers (Micosoft calles them Morts) are not going to buy this at all. If forced to accept it, they will bitch all the way to the bank or the unemployment line (whichever comes first).
So why the hell would a corp want to go through such development difficulties and time investment? Why would a simple financial corp want a skinnable app for a simple line-of-business CRUD form? Most wouldn't. If you are writing a vendable product, maybe you do want to go to this expense. If you are doing departmental SQL CRUD forms, it is difficult to justify.
The ultimate reason a corp like ours does this is because you have two ambitious programmers who are determined to stay up-to-date, and have no intension of becoming obsoleate, outdated, antiquated lasy Microsoft Morts.