Single Region Multiple Modules - One at a time...

Oct 14, 2008 at 12:11 AM
Edited Oct 14, 2008 at 8:20 AM
Hi guys,

I have been trying to find a simple solution but to no avail. There are hints here and there but nothing solid, almost bothers me that how come Prism just left that little part out, or maybe its me who is totally dumb! Here is the scenario:

  1. Main SHELL App
  2. Three regions, for simpilicty "MenuRegion", "MainRegion", and "FooterRegion"
  3. Multiple modules like "ModuleA", ModuleB", "ModuleC", "Module(n)"...
  4. In the "MenuRegion", I have three Buttons that are associated to unique modules "ButtonA", "ButtonB", and "ButtonC" etc.
  5. When pressed, each button loads a corresponding Module(x) in the region "MainRegion", and the previously loaded Module disappears.
  6. Previously loaded Module should be able to reappear when the corresponding Button(x) is pressed etc.

Simple? Sure... but how to do and what Control Container to use?

Currently I have a working project with this setup. Loads each module Dynamically and that's how I want to keep it. The only part I dont get is how to make these Modules Appear/Dissappear with "Menu" Buttons in the same region ("MainRegion")?

What I am looking for is:

Simple and elegant solution to enable this scenario. Anyone?

Cheers!

Oct 14, 2008 at 6:28 AM
Edited Oct 14, 2008 at 6:32 AM
O.k.  I've been struggling with this - I think I want to do the same you do.  I created an ApplicationServicesModule, with a XAML with a toolbar with 2 buttons for ModuleA, ModuleB.

I create the ApplicationPresenter with a View that has RegionManager and Container properties:

 

public class ApplicationPresenter: IApplicationPresenter

 

{

public ApplicationPresenter(IApplicationView view, IRegionManager regionManager, IUnityContainer container)

 

{

 

this.View = view;

 

this.View.RegionManager = regionManager;

 

this.View.Container = container;

 

}

 

public IApplicationView View { get; set; }

 

}

The View then has the following code in click events (I haven't properly used commands, I wanted to get this out as a PoC before I spent too much time refining my approach):

 

 

public partial class ApplicationView : UserControl, IApplicationView

{

 

public IRegionManager RegionManager { get; set; }

 

public IUnityContainer Container { get; set; }

 

public ApplicationView()

{

InitializeComponent();

 

private void button1_Click(object sender, RoutedEventArgs e)  

{

 

IRegion mainRegion = RegionManager.Regions[RegionNames.MainRegion];

 

var clientView = RegionManager.Regions[RegionNames.MainRegion].GetView(ViewNames.ClientView);  

 

if (clientView != null)

{

mainRegion.Remove(clientView);

 

var dashboardView = RegionManager.Regions[RegionNames.MainRegion].GetView(ViewNames.DashBoardView);

 

if (dashboardView == null)

{

 

IModule dashboardModule = this.Container.Resolve<PACProto.Modules.Dashboard.DashboardModule>();

dashboardModule.Initialize();

}

 

else

{

mainRegion.Activate(dashboardView);

}

}

So if you click on a button for ModuleA (here it is DashboardView), we remove the ModuleB view (ClientView) and check for DashboardView and initialize.  I don't if there is a cleaner way, but this works.

Bootstrapper only loads the ApplicationServicesModule.

 

 

 

Oct 14, 2008 at 2:25 PM

Hi,

 

Remember that modules are not loaded into regions, views are. If each module contains one “main view”, what you could do is place a ContentControl for the MainRegion, which allows just one view to be the active (thus displayed) view. Then, when the button of a module is clicked, you could try to retrieve the view of that module from the region and show it, but if it is not present, you should create it. The code of the click handler should look like this:

 

void buttonA_Click(object sender, System.Windows.RoutedEventArgs e)

{

    ViewModuleA view = _regionManager.Regions["MainRegion"].GetView("ModuleAMainView") as ViewModuleA;

 

    if (view == null)

    {

        view = _container.Resolve<ViewModuleA>();

        _regionManager.Regions["MainRegion"].Add(view, "ModuleAMainView");

    }

 

    _regionManager.Regions["MainRegion"].Activate(view);

}

 

Hope it helps.

 

Ignacio Baumann Fonay

http://blogs.southworks.net/ibaumann/

Oct 14, 2008 at 3:10 PM

So does using another Module to manage the application make sense?  In my bootstrapper, I only initialize the ApplicationModule, whose constructor takes the IUnityContainer and IRegionManager.  Then in my ApplicationView I have a MenuToolbarView which has a MenuToolBarPresenter that uses the Container and RegionManager to activate/deactivate views now.  In my ApplicationModule I’m dynamically loading the modules on first menu click (I don’t know of a way to get the RegionManager object in the main app project – maybe make it a property on the shell?) I will make this more DI once I get this technique for loading/unloading views.

The ContentControl does the trick.  I gave up on Deactivate b/c Region.Deactivate threw an exception with ItemsControl, and I thought it was not implemented yet.  Deactivate on a ContentControl region does work though:

IRegion mainRegion = RegionManager.Regions[RegionNames.MainRegion];
var clientView = RegionManager.Regions[RegionNames.MainRegion].GetView("ClientView");
if (clientView != null)
{
    mainRegion.Deactivate(clientView);
}


var dashboardView = RegionManager.Regions[RegionNames.MainRegion].GetView("DashboardView");
if (dashboardView == null)
{
    IModule dashboardModule = this.Container.Resolve<PACProto.Modules.Dashboard.DashboardModule>();
    dashboardModule.Initialize();
}
else
{
    mainRegion.Activate(dashboardView);
}
Oct 14, 2008 at 8:44 PM
Edited Oct 14, 2008 at 8:45 PM
Hey thanks guys....

Both and all solutions up there work. Afterall I was doing a dumb thing to begin with, as ibaumann says: "Remember that modules are not loaded into regions, views are" was my problem all along...

Instead of loading Views into the region, I was loading Module instead! Duh! Wrong type ;)


Lesson learned: When tired and frustrated while coding, take a break and go for a stroll or a long drive...
Oct 14, 2008 at 8:55 PM
Edited Oct 14, 2008 at 9:04 PM

Hi, drive

 

In case you are going to deactivate views, it is fine to use the ContentControl, as in the ItemsControl, the Deactivate method throws an InvalidOperationException exception because the ItemsControlRegionAdapter uses an instance of the AllActiveRegion class, which states that all views shown in the region are active always. Therefore, you cannot deactivate a view.

 

Having a module to control the application can be useful in some scenarios. Depending on the complexity, you may resolve a custom class via the container in the Shell project in order to get the RegionManager and the UnityContainer for the same controlling purpose. Another valid approach would be to provide the Shell with a Presenter (as shown in the Stock Trader Reference Implementation) or with a PresentationModel (as shown in the blog post Presentation Model with DataTemplates in CompositeWPF (Prism) Sample). In that way, you will be able to get the RegionManager via Dependency Injection.

 

Please, let me know if this helps.

 

Ignacio Baumann Fonay

http://blogs.southworks.net/ibaumann/

Oct 14, 2008 at 9:08 PM
True. Also in my case I am using ContentControl, which is what I want. For this Deactiveate(view) works. But wont work for ItemsControl. For removing Views from ItemsControl, I loop through Views Items, and then Remove(view) them as needed, or for all.

Cheers...