Tabcontrol+ Scoped Regions+ Dynamic Regions + RegisterViewWithRegion

Topics: Prism v4 - WPF 4
Jun 18, 2013 at 10:41 PM
Edited Jun 18, 2013 at 10:48 PM
Hi,

I am again stuck in scoped regions. I am using Prism 4.1 with MEF and ViewModel-First approach.
I have a TabControl - regionName is set as "TabRegion" and I add a MainView (with nested regions in it) to my TabItem.

The structure looks like this:
  • ShellView:
  • * MenubarRegion
  • * * MenubarView(Singleton)
  • * TabRegion
  • * * MainView(newviewforeverytabItem)
  • * TopRegion
      • ChildView(newviewforeveryMainView)
ShellView (Window):
<DockPanel>
         <ContentControl DockPanel.Dock="Top" prism:RegionManager.RegionName=“MENUBAR_REGION“/>
        <ContentControl DockPanel.Dock="Bottom" prism:RegionManager.RegionName="TAB_REGION"/> 
  </DockPanel>
MainView: (UserControl in MainModule.dll)
    <DockPanel >
         <TextBlock Text="{Binding LocalRegionManagerHashCode}" DockPanel.Dock="Top" />
         <ContentControl prism:RegionManager.RegionName=“TOP_REGION“
                           prism:ClearChildViewsRegionBehavior.ClearChildViews="True"    DockPanel.Dock="Bottom" 
                           prism:RegionManager.RegionManager="{Binding LocalRegionManager}"/>
</DockPanel>
ChildView: (UserControl in ChildModule.dll)
<StackPanel Orientation="Horizontal">
                 <TextBlock Text=“My Name: " FontSize="32"/> 
                <TextBlock Text="{Binding Name, Mode=OneWay}" FontSize="32" FontWeight="Bold"/> 
</StackPanel>
In MainViewModel constructor:
[I can create MainView or Inject it using MEF DI container and I create a new scoped regionManager in the constructor].
globalRegionManager.Regions[“TAB_REGION”].Add(new MainView(),null true);
I do not do anything in MainViewModule's Initilize() API.

In ChildView as UserControl
private static int count=0;
public ChildView(){
InitializeComponent();
count++;
this.Name=“ChildView:”+count;
}
Scenario# 1:

In ChildViewModule's Initilize():
globalRegionManager.RegisterViewWithRegion(“TOP_REGION”, GetViewType);

private object GetViewType(){
return   ServiceLocator.Current.GetInstance<ChildViewModel>().View;
}
Output: Working as Expected

Whenever I add a TabItem, new instances of MainView and ChildView are created and have the appropriate ScopedRegionManager property. Hence a new ChildView is shown in all tabItems (I ascertain this by the ChildView.Name, which is different in diff tabs).

Scenario# 2:

In ChildViewModule's Initilize(): empty
In ChildViewModel constructor:
(Again, ChildView can be injected or created explicitly)
globalRegionManager.Regions[“TAB_REGION”].Add(new ChildView(),null true);
Output: Failure

Whenever I add a TabItem, new instances of MainView and ChildView are created, but when I switch from current tab to next tab, the new tab's scoped regionmanager somehow adds (in the TOP_REGION) previous tab's childView as well as its own childView, but the active view is previous tab's childView which is eventually displayed.

This removes the previous tab's childView from its TOP_REGION. Switching back to it would not display it again. And the new childView is no where displayed.

Requirement:

We are developing an application where we want to read the regionName and view initialization parameters from a config file, hence we want to register the ChildView through ChildViewModel where we have all the required information from the config rather than from Initialize().

Question:

Only difference between these two approaches is that in scenario#1 I am using overriden Initialize() method to register the view with the region, whereas in scenario#2 I am registering the view in ChildViewModel's constructor (this is called whenever ChildViewModel is created, which happens for every tab).
  • Is scenario#2 approach correct? If not, what is getting wrong here?
  • How can I dynamically add regions where contentcontrol with a region is not yet added to the visual tree.
Please let me know if I can provide more information.

Thanks,
RDV
Jun 19, 2013 at 2:39 AM
I understood my mistake in scenario # 2:
globalRegionManager.RegisterViewWithRegion(“TOP_REGION”, GetViewType);
Will add the said view to the TOP_REGION everytime it is called, hence I am seeing the failure case.

But my requirement remains the same. I want to be able to dynamically add views to the regions where views are first initialized based on a config file.

I realized I need View Injection and later View Activation in my ChildViewModel constructor:
            this.View=new ChildView(); //can get this object via DI Container as well.
            regionName=getRegionNameFromConfigFile();
            IRegion region = regionManager.Regions[regionName];
            region.Add(this.View);
            region.Activate(this.View);
The only problem with this approach is that it complains that regionName does not exists in the regionManager which is valid since DelayedRegionCreationBehaviour does not create the region unless the control is added to the visual tree.

As a workaround, I have created a DummyModule which has a DummyView and in DummyModule's ovveride Initialize() API:
regionManager.RegisterViewWithRegion("TOP_REGION", typeof(DummyView));
This ensures that TOP_REGION exists before view injection in ChildViewModel's constructor. Hence my issue is resolved.

I am wondering what could be a better approach to do this? May be remove DelayedRegionCreationBehaviour? Anything else?

Thanks,
RDV
Developer
Jun 19, 2013 at 8:07 PM
Hi RDV,

I'm glad you could find the cause behind your problem.

A possible workaround to be able to inject the ChildView into the region is to wait for the region to be created before injecting the view. For example, in the constructor of the ChildViewModel you could check if the region was created or not (using the ContainsRegionWithName method of the region collection). If the region exists you just need to add and activate the view. If not, you can subscribe to the CollectionChanged event of the region collection in order to be notified when a region is created and then inject the view.

I hope this helps,

Damian Cherubini
http://blogs.southworks.net/dcherubini