Separation of Concerns

Topics: Prism v2 - Silverlight 3
Mar 23, 2010 at 4:44 AM
Edited Mar 23, 2010 at 5:44 PM

Hello

I have a question that I am hoping the community can help me resolve...

My shell application is comprised of a "MainRegion" which is visually represented by a TabControl. The TabControl's two TabItems are each populated by instances of views from two separate modules (let's call them ModuleOne and ModuleTwo). In a separate module, (let's call this one TreeModule) I have a TreeControl view and all the necessary logic to fill the tree view with data.

I would like the views from ModuleOne and ModuleTwo to each display an instance of the TreeModule’s view. I have declared a Region (inside a ContentControl) in each of the views of ModuleOne and ModuleTwo. This is where I would like to load an instance of TreeModule view. My question is about the proper way to accomplish this.

My requirement is to maintain a clear separation between the TreeModule the other Modules (I believe the TreeModule should be reusable for future applications).

I have started by creating a scoped region manager in the Initialization method of ModuleOne and ModuleTwo.  It seems that I now need to add an instance of the TreeModule's view to the proper region within the scoped region manager(s).

How can I do this without directly referencing the TreeModule from within ModuleOne and ModuleTwo? In order to resolve using the Unity, I would need to at least have access to the view's interface. I have considered adding the Interface for the view to the common infrastructure project. Then I could use it to resolve using the container. Would this be the correct solution?

Also, in many of the examples I have seen online, modules (like my TreeModule) often add themselves to regions within the injected RegionManager. In my current situation, this causes an error as the Shell’s Region Manager does not have a named region for the Tree View (because the views within the TabItems do). This leads me to believe I should be registering the scoped region managers and injecting those into the TreeModule? However, that seems incorrect as well because I believe the application should have only 1 instance of the module.

I am confused! Any help will be really appreciated.

---Edit---

Here is a code snippet of the Init Method for ModuleOne (variable names changed for simplicity)

// ModuleOne's Init Logic 
//(ModuleOne's view represents one TabItem in a TabControl)
_container.RegisterType<ISomeModel, SomeModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<ISomeViewModel, SomeViewModel>();
_container.RegisterType<ISomeView, SomeView>();

var mainView = _container.Resolve<ISomeView>();

// this sets the datacontext of the view to the viewmodel
mainView.ApplyModel(_container.Resolve<ISomeViewModel>());

// add to main region and create a scoped region manager for the nested regions
IRegionManager scopedRegionManager = 
    _regionManager.Regions["MainRegion"].Add(mainView, null, true);

// adding treeview to scoped region 
// I have to directly reference the TreeModule in ModuleOne to get these
// types and that seems like too much coupling var scopedTreeView = _container.Resolve<ITreeView>(); scopedTreeView.ApplyModelITreeViewModel>()); scopedRegionManager.AddToRegion("TreeView", scopedTreeView);

 

Mar 25, 2010 at 12:30 AM

Hi,

The usual approach followed in modular applications is that modules register only their own views into regions. This is done to avoid breaking the decoupling between modules, as for example if ModuleOne knows about TreeModule’s views they become dependent on each other (if TreeModule isn’t there, then ModuleOne cannot work).

As you said, you would get an error if you tried to add the TreeView to the application’s RegionManager, as the region you want to register it in is in another RegionManager. The approach you suggested about registering the scoped RegionManager instances is the one I would follow. Guido Maliandi talks about how to achieve that in this forum thread.

You can then resolve the RegionManager instance required and use View Injection to add the TreeView to the necessary region.

Please let me know if this helps.

Damian Schenkelman
http://blogs.southworks.net/dschenkelman

Mar 25, 2010 at 8:58 PM
Edited Mar 25, 2010 at 8:59 PM

Damian

Thank you for helping me out with these concepts! The information definitely helps, but I am left with an additional question; please allow me to explain...

In both ModuleOne and ModuleTwo's Initialize, I will go ahead and register named, scoped Region Managers with Unity:

(ModuleOne - Initialize)

IRegionManager modOneScopedRegionManager =
    _regionManager.Regions["MainRegion"].Add(mainView, null, true);

this._container.RegisterInstance<IRegionManager>("ModOneRegionManager", modOneScopedRegionManager);

(ModuleTwo - Initialize)

IRegionManager modTwoScopedRegionManager =
_regionManager.Regions["MainRegion"].Add(mainView, null, true);

this._container.RegisterInstance<IRegionManager>("ModTwoRegionManager", modTwoScopedRegionManager);

However, I am confused as to where in the hierarchy I could resolve the named RegionManager instance(s) and get those injected into something from the TreeModule that would provide me a view instance. Currently, my TreeModule is being loaded "WhenAvailable" by Prism and the TreeModule's Initialize method is receiving the app's main region manager.

I guess one possible approach could be to publish an event (using EventAggregator) in ModuleOne/Two's Initialize. The event would have a payload consisting of the scoped RegionManager. If the TreeModule were subscribed to this event, it could do the view injection in the handler.

Would this be a good approach and thanks again Damian for your time!

 

Mar 28, 2010 at 7:42 PM

For anyone else who has encountered this issue, I found another thread that seemed to validate the method I discuss above regarding the EventAggregator:

http://compositewpf.codeplex.com/Thread/View.aspx?ThreadId=62400

 

The answer I was looking for was provided by dschenkelman:

When two modules reference each other, they are very tight coupled which goes against the modularity guidance. Think of it as if you were to completely remove a module from your application, it should still work. It would not have full functionality, but no changes would be required (except modules to be loaded, of course).

Take into account that the Quickstart application’s objective is showing a particular feature or capability of the guidance, in the simplest way possible. A good approach for this situation could be firing an event using EventAggregator (to communicate between modules), so when the new view has to be injected the module that has it can be notified by the module where the action is initiated. As you can see, this approach would have mixed EventAggregation with UIComposition, thus missing the important point of the Quickstart.

So, thanks again Damian!!

Rob