ContentControl Direct View Binding

Topics: Prism v4 - WPF 4
Feb 8, 2012 at 3:06 AM

We have a WPF Prism (using Unity, DI & MVVM) application that we are developing as a POC. We do not want to be forced into defining regions on the Shell.xaml because we want/need the flexibility to load views in different configurations. What we are doing is defining a single ContentControl on the Shell and then loading a View from a separate Class Library that defines a particular configuration of ContentControls / Regions. This allows us to then load a different View on the Shell that has a yet another configuration of ContentControls.

I have seen a number of tracked issues/bugs with the use of named Regions using the RegionManager/IRegion in which to inject (direct) or register (passive) Views.

Not to mention the following considerations:

- Maintaining static/const string definitions for all regions

- Keeping clean separation of scope/visibility, I dont want other developers attempting to reference a region that does not exist.

Method

I actually have started using a pure binding method that does not require me to manage const string names for all of possible Region Names.

Consider the following:

Assume Unity and the use of DI to join all Views/ViewModels

For code to follow assume UC = IUnityContainer & EA = IEventAggregator

Assume that when the container (UC) is used to Resolve<MyView> in the following code, the Code Behind constructor using Dependency Injection to set it's own DataContext to a ViewModel registered with Unity.

Assume a shared CompositeWPF event named ShowMainView<UIElement>, The payload is of Type UIElement (Unity will be used to resolve this payload)

The Shell Project's Bootstrapper Loads a Module defined in a separate Class Library, which has Views/ViewModels defined. This Module publishes a Prism Event as follows:

Separate Class Library (Module Project)

EA.GetEvent<ShowMainView>().Publish(UC.Resolve<MyView>());

 

Shell Project> Shell.xaml

<ContentControl Content="{Binding MainContentArea}" />

ShellViewModel.cs

Has a public property: public UIElement MainContentArea {get;set;}

Subscribes to ShowMainView event: EA.GetEvent<ShowMainView>().Subscribe((V)=>{ MainContentArea = V });

I am trying to keep this short & I hope that you all can infer the code that I left out as I am trying to focus on the point which is Binding the Content of a ContentControl to its parent View's DataContext which is a ViewModel that has a defined public property of type UIElement that is set to a View object that is Resolved using Unity & Dependency Injection. The Prism Event is a way to loosely communicate from an external Class Library that publishes a Prism event with a payload of a View resolved by Unity, where this View is then shown by a subscribing ViewModel, such as the ShellViewModel in this example ...

EA.GetEvent<ShowMainView>().Subscribe((V)=>{ MainContentArea = V });. 

This works great and I am wondering what comments, objections you all might have, thanks.

 

Developer
Feb 8, 2012 at 8:13 PM

Hi,

Prism by design can be used in a granular way depending on your personal preferences and the requirements of your scenario. Hence, I believe the approach you mentioned seems to be a valid one.

On the other hand, take into account that, when using Prism Regions, you can benefit of the features that Prism provides out of the box, such as the Navigation API, region behaviors, etc.

If you are interested, more information about this features can be found in the following resources:

Thanks,

Agustin Adami
http://blogs.southworks.net/aadami

Feb 12, 2012 at 2:53 AM
Edited Feb 12, 2012 at 3:23 AM

Please understand that I am looking to talk this through for the sake of learning and comparing/contrasting these somewhat subjective techniques. My goal is not to say that what I am proposing is better, but rather to verify that it is valid along with your objective feedback on the pros/cons.

Regarding the documentation on Region Navigation and the passing of parameters during navigation I want to compare what I am describing above (in the original post) in parity with what is documented in Chapter 8.

The ability of any module to use the RequestNavigate requires passing in a named string (and named region as a string if called from the region manager). Hard coded strings always feel 'bad', especially when loaded Views themselves contain regions. I have yet to see a scoped const/static region name definition pattern that suites all cases as your application grows and changes in ways that you cannot foresee. Passing parameters via query string construct that again are retrieved via "string" as key/value pairs feels like limitations of the asp.net world. In my constructive opinion, using 100% complex objects and xaml binding you can perform all of the tasks of the Region Manager without the overhead of maintaining const/static Region names & class decorators in order to enable certain behaviors - While enabling (possibly) more power in terms of typed objects with what can done within the Prism event payload. By allowing the ViewModel of any View that has named content areas such as ContentControls to define a UIElement Binding and a Prism event subscription it appears that you can solve all of the UI scenarios that could also be solved using Regions. I am depending on readers of this post to infer the implementation pattern of what I am implying, as once this convention is defined there is really no limit to the number of content areas and Views/Sub Views that can be added. Furthermore, by documenting each Prism event you are building a typed library to which future Views/ViewModels could tap into in a disconnected/abstracted way. For example, I create a new View/ViewModel that has 3 brand new ContentControls. This view can show some of the Views that my existing Views show within their ContentControls. Using the proposed technique there would be zero maintenance outside of the new objects (View/ViewModel) b/c the ViewModel would just subscribe to the existing Prism event and pull the UIElement out of the payload (via defined interface that all payload objects would implement). Using the region manager would require the Region names to be tracked and therefore updated somewhere. If additional parameters are needed, then using the proposed technique a typed complex payload object can be passed/received. Whereas with the Region Manager we again have to maintain more string values (somewhere) for the Key/Value pairs. A final point to make in favor of the proposed technique is that since the publication of the Prism event contains the View, other living objects can also listen (perform a type check if needed) and take action (as just an added technique to solve problems) in a very loosely coupled way due to the event driven nature of this technique (I do not believe INavigationAware provides as much flexibility as Prism events).

Questions/Cons

Are there any Prism event performance limitations or considerations that I should be aware of when publishing events where the payload is a View that has been Resolved via Unity in comparison to use with the Region Manager.... Consider the following:

XAML

<ContentControl RegionManger.RegionName="MainRegion" />

From any given Module level code: RegionManager.RequestNavigate("MainRegion", UC.Resolve<MyView>(), NavigationCompleted);

From the same code: 

private void NavigationCompleted(NavigationResult result){

// Interaction/event code could go here...

}

 

...versus...

 

XAML

<ContentControl Content="{Binding UIElementProperty}" />

From any given Module level code: EA.GetEvent<ShowMainView>().Publish(UC.Resolve<MyView>());

From subscribing ViewModel code: 

EA.GetEvent<ShowMainView>().Subscribe((f)=>{

UIElementProperty = f;

// Interaction/event code could go here...

});

Developer
Feb 13, 2012 at 8:51 PM

Hi,

So far, beside the aforementioned comments, I couldn't find a major limitation to follow this approach.

On the other hand, one thing that must be considered, is that when the content property is changed in the corresponding view model you must ensure that the UI gets properly notified of these changes, otherwise they won't be reflected to the UI. For example this can be done by calling the RaisePropertyChanged method if your view model derives from the NotificationObject class provided by Prism.

For those interested, I created a simple HelloWorld application, following jesjones proposed approach. You can find it, in my Skydrive account under the name "ContentControlDirectViewBindingSample"

Thanks,

Agustin Adami
http://blogs.southworks.net/aadami