PRISM V4 Drop 2 : MVPVM Modularity with MEF

by 26. June 2010 15:35

Sample Application: ModularityWithMef.zip (4.95 mb)

Although I have been a hard-core Unity (Dependency Injection) cronie since its conception I have to admit I was very pleased with MEF extensions to PRISM.  However, it did force me to rethink some design strategies that I've used since the Smart Client Software Factory - such as using generics to quickly wire-up base classes (you can't use generics with attributes).

Somewhat reluctant at first I scraped my earlier prototype/design and moved forward with a new design that is centered around MEF and PRISM V4 Drop 2.   I'll have to build an infrastructure that will minimize refactoring when PRISM V4 is released; particularly since PRISM, MEF and Unity will serve as the main tools.

The sample that is provided in the PRISM V4 Drop 2 looks as follows with the exception of the View on the right.  I added a new new "Hello World" view utilizing the MVPVM pattern; I use PRISM/MEF to wire-up the infrastructure.  

Note: Module F Imports two Business Logic Layers (DoctorBLL/PatientBLL) which in turn Import their respective Data Access Layers.  It then uses these, within its presenter, to populate the view model properties that the two ComboxBoxes are bound to. 

The end result after refactoring the sample is as follows:

  • The Model instantiates the Presenter 
  • The Presenter instantiates the View and wires up the view model as applicable

After wire-up the Presenter handles all business logic (you'll find no code in the code-behind files).   Below is all of the code required to wire-up the module A view with A)dd, E)dit, D)elete and P)rint buttons.   Modules A and E both use the same CRUDControl and each have their own CRUD View Models (derived from CRUDViewModelBase) which permit base functionality to be overridden, i.e., notice that Module E does not have a P)rint button.   The only wire-up required for the CRUDControl is shown on line 23 below where the DoctoCRUDViewModel.ParentViewModel is set with a reference.

The only issue I ran into, which will result in a message in the user forum is I couldn't figure out how to [Import] parts in the Bootstrapper - so currently there is some code in the MainWindow code-behind but as soon as I work this out I'll move what little code remains there into the Presenter.

Sample Application: ModularityWithMef.zip (4.95 mb)

Tags:

MEF | MVPVM | Prototype

MVPVM: Sharing a CRUD control with multiple views

by 20. June 2010 13:45

The object is to reuse CRUD logic, such as enabling/disabling buttons, setting IsEditing flags that XAML can bind to, etc, without polluting our view models.   For this we have a CRUDViewModelBase class that we derive from and set flags based on our needs - in the example below (right image) we set IsPrintVisible to false effectively hiding the Print button for the Patient (Module D in this prototype application DirectoryLookupModularity.zip (5.82 mb) ).

Note that the DoctorViewModel and PatientViewModel have overrides for each of the button clicks as well as a reference to it's parent view model and shared criteria view model.   The CRUD logic is completely decoupled and can be encapsulated within each view model for each concern; we have a clear separation of concerns (SOC).

Once we assign the PatientCRUDViewModel to Module D's View we can see below that the [P] button is not visible.   The default behavior is to have all buttons visible as shown for Module A

Since the Modules job (ModuleA.cs in image below) to load the Presenter, and the Views (DefaultViewA.xaml) have no code in their code-behinds, it is the Presenter's (DoctorPresenter.cs) responsibility to execute business logic as required.  Below we can see where the doctorCrudVm.ParentViewModel is provided a reference to its parent view. 

A single presenter / view can handle multiple CRUD requirements easily using the CRUDControl.

Note: Where a View can only have one view model, a view model can be shared across numerous views (reused).  In the case of our sample application the Shell and Modules B and C all share the CriteriaViewModel which is defined in the bootstapper as a singleton (the MVPVM framework represented in the sample link above easily supports this). 

It is the ability to reuse this domain object, across multiple views and modules, that dictates our project structure.  The PrototypeApp.Interface contains all the classes used by this application (ideally I would have the Business and Data layers in their own projects so they can be reused across multiple solutions but this is just a sample prototype).

A key point not to miss here is that you don't want to populate your view model domain objects with business logic that does not pertain to managing its view.

DirectoryLookupModularity.zip (4.91 mb)

MVPVM: Integration testing using the Presenter

by 20. June 2010 12:17

The MVPVM pattern lends itself to effective integration/unit testing.   Since their is no code in the code-behind and the Presenter processes all business logic against the ViewModel and View it is the best component to perform integration test against. 

In the following application (download from this link DirectoryLookupModularity.zip (5.82 mb)) there are three view models:

  1. CriteriaViewModel - utilized by views E, B and C
  2. DoctorViewModel - utilized by view A
  3. PatientViewModel - utilized by view D

The MVPVM framework used for the attached application consistently uses the following for bootstrapping modules:

The ModuleBase<TPresenter> (see below image) instantiates the specified Presenter, e.g., on line 14 below we identify the "DoctorPresenter" as the presenter.

The PresenterBase<TView, TViewModel> (see below image) specifies the View and it's ViewModel which in this case is DefaultViewA and DoctorViewModel respectively.  On line 23 we register the view (DefaultViewA) with the TopLeft region which was declared in the Shell's XAML.  The remaining logic is the code required to wire-up a CRUD control and to get the current module load counter value so that we can see that the module dependency attributes are working correctly.

These baseclasses do all of the wiring up so that we can focus on business rules; we don't spend a lot of time getting our module up and ready for the requirements at hand.

The integration test for Module A's presenter is shown below (lines 46-52); it verifies that the infrastructure properly wired up the View and ViewModel. 

Did you notice that on line 24 above we set the container to null and on the following line use the container.RegisterServices() method?   On the surface this may look odd but it lends itself to testing the Prism environment.

We use the same RegisterServices in our bootstrapper as we do in our integration/unit test - we do this to ensure our environments match for testing purposes.  To accomplish this we use a IUnityContainer Extension class (IUnityContainerExtension), the only time container will be null is during testing so we instantiate a MockBootstrapper (lines 25-27 below).   From that point on we have the same registrations to ensure our test will run without complaint.

WPF assembly: XmlnsDefinition - identify types for XAML usage

by 20. June 2010 09:42

If a picture says a thousand words then the following should save me some typing as to the value of XmlnsDefinition (reference line 5 below) - it provides an easy to remember Url mapping to your namespaces. 

My PrototypeApp (born from the Prism DirectoryLookupModularity sample) has an Prototype.Interface project where much of the application specific code resides.   Within the AssemblyInfo.cs file (under the Properties folder) I was able to map all of the applicable namespaces to a single, easy to remember, url.

Excerpt from AssemblyInfo.cs follows: 

[assemblyAssemblyVersion("1.0.0.0")]
[assemblyAssemblyFileVersion("1.0.0.0")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp,
                            "PrototypeApp.Interface.Constants")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp
                           "PrototypeApp.Interface.ViewModels")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp,
                            "PrototypeApp.Interface.Controls")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp
                           "PrototypeApp.Interface.Converters")]

Demo source code follows: DirectoryLookupModularity.zip (5.82 mb)

Notice

Blog videos and references to CodePlex projects are no longer valid