multiple views in tabItem using prism

Topics: Prism v4 - WPF 4
Sep 1, 2012 at 11:21 AM

I'm fairly new to Prism. any help would be greatly appreciated. My requirement: every tab I open will have main region to load a grid with some data. depending on requirements, I need to load another view on the left like a searchpanel. (set of vertically aligned dropdowns and a search button). Not all tabs might have this left region.

My question is in terms of organising regions and views. I'm reading about scoped regions, tab's datatemplates to organise views. I'm confused what to use when. please help.

Because not every tab might have left region, I'm not sure how to organise views and regions. desperate for some advice.

Left region doesn't sit outside tab. but within tab.

I have also posted this in stackoverflow. I'm desperate for some help 

http://stackoverflow.com/questions/12211854/multiple-views-in-tabitem-using-prism

 

Kavya

Sep 3, 2012 at 4:51 AM

Hi Kavya,

What you would need is something like this.

TabItem

  • MainView (Loaded in MainRegion)
    • Grid with 2 columns : 1st column a contentcontrol with LeftRegion, 2nd column your datagrid

Now when you load tabitem, if you are using MEF, you could do regionManager.RequestNavigate("MainRegion", new Uri("MainView", UriKind.Relative). Now in the OnNavigatedTo in the MainView you can have your logic to decide which view you want to load in the LeftRegion. And then you just have to do regionManager.RequestNavigate("LeftRegion", new Uri("SomeView", UriKind.Relative), where the regionManager is injected into MainView.

// Load main view in MainRegion
_regionManager.RequestNavigate("MainRegion", new Uri("MainView", UriKind.Relative));

// Main View & ViewModel
[Export("MainView")]
public partial class MainView
{
   [ImportingConstructor]
   public MainView(MainViewModel viewModel)
   {
        InitializeComponent();
        DataContext = viewModel;
   }
}

[Export]
public class MainViewModel : INavigationAware
{
     private IRegionManager _regionManager;

     [ImportingConstructor]
     public MainViewModel(IRegionManager regionManager)
     {
           _regionManager = regionManager;
     }

      public void OnNavigatedTo(NavigationContext navigationContext)
        {
            // DO you access checks and load the view in leftregion
           _regionManager.RequestNavigate("LeftRegion", new Uri("SearchView", UriKind.Relative));
        }

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext) {}
}

// Search view & viewmodel
[Export("SearchView")]
public partial class SearchView
{
   [ImportingConstructor]
   public MainView(SearchViewModel viewModel)
   {
        InitializeComponent();
        DataContext = viewModel;
   }
}

[Export]
public class SearchViewModel : INavigationAware
{
      public void OnNavigatedTo(NavigationContext navigationContext)
        {
     
        }

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext) {}
}

 

Hope this is clear.

Cheers!

Ganesh

Sep 3, 2012 at 7:22 AM

Hi Ganesh, 

Thanks for taking time. your content control tip helped me. I was looking more from the design perspective.

like.. would I need scopedregions for my left search panel? Tabs get loaded when we click one of the button in the menu bar but not from the main region.

but all tabs won't have left panel. I want to be able to load the left view based on few business decisions. like a simple flag for eg.

just trying to connect the dots. if you can shed some light on these aspects would be helpful.

Would I need scoped region within this contentcontrol?(as I dynamically need to load the view?)

Kavya

Sep 3, 2012 at 7:31 AM

As far as I see it really doesnt matter how your tab is loaded. What I would be concerned about is how you want to load your views in a tabitem. That is precisely what I have shown above. You would do the above on a tabitem loaded or something.

Regarding scoped regions, you dont really need it. If you inspect the regionManager, when your MainView is loaded in the Mainregion, it would have already registered the LeftRegion in its regions collection. 

Your business logic can drive whether you want to load something in the LeftRegion or not. It can always remain there, as it is a contentcontrol in grid column. You could set your columnwidth to auto, so it wont occupy space in your page when you dont load view in it.

One thing you might need to do is when you navigate away from the MainView you should remove the LeftRegion from the regionmanager in the OnNavigatedFrom event. Else you could get regioncreationexceptions when you load the MainView the next time.

if (_regionManager.ContainsRegionWithName("LeftRegion"))
 _regionManager.Regions.Remove("LeftRegion");

Sep 3, 2012 at 8:20 AM

Thanks Ganesh, 

I get what you saying. may be it's my lack of wpf knowledge. but if I've 

MainView (Loaded in MainRegion)

  • Grid with 2 columns : 1st column a contentcontrol with LeftRegion, 2nd column your datagrid

left region is fine. i can load any view i want. but i'm guessing i need another content control(in column 1) instead of datagrid? as datagrid would be another user control? 

I'll try your approach. 

Sep 3, 2012 at 8:21 AM

would you help me understand when should I exactly use scoped regions then? I've read numerous articles but still can't comprehend. they just say if I need to load a view dynamically, i could use scope regions.

Sep 3, 2012 at 8:22 AM

Yes. If you Right side is also going to be usercontrol (view) being dynamically loaded, then yes you can have another ContentControl there with a RightRegion.

Sep 3, 2012 at 8:25 AM

I would probably use scoped regions when I'm adding regions dynamically and not when loading views in a region dynamically.

Sep 3, 2012 at 8:30 AM

Thanks Ganesh. 

Your insight indeed helped me understand. will Take it from here. lets see how I get on. UI has never been my area. so struggling with it a bit.

Thanks again

Sep 3, 2012 at 8:31 AM

Glad I could help.

Sep 3, 2012 at 2:35 PM

I'm now one step further.

This is what I have so far:

In a tab. the main region has grid in it(View A). when i click on a row, it should open another tab(view B). Essentially another view.

I had got this working. (I've based it on stocktrader including the headerinfo of tab).

Now, I need above behaviour with left search panel in every tab. as you suggested, i did below. Behaviour is awful.  I understand what's going on. but no clue how to fix. 

 I have below xaml. both the contentcontrols are loaded. however, I think i'm binding it wrong or not sure. when I click on a row in the main region, the next view (view b) open in same tab.

View A and view B are  are loaded into subMainRegion where as search panel is loaded into left region. With below xaml, I also lost the headerInfo on a tab.

Please help.

<Window x:Class="ReleaseManagement.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://www.codeplex.com/CompositeWPF"  
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Shell" Height="768" Width="1024">
    <Window.Resources>
        <Style TargetType="{x:Type TabItem}" x:Key="ShellTabItemStyle">
            <Setter Property="Header"
                Value="{Binding RelativeSource={RelativeSource Self},Path=Content.DataContext.HeaderInfo}" />
            
           
        </Style>
    </Window.Resources>
    
       <!--<ItemsControl prism:RegionManager.RegionName="MainRegion">-->
        <TabControl x:Name="MainRegionHost" prism:RegionManager.RegionContext="{Binding Delivery}"
                     prism:RegionManager.RegionName="MainRegion"  ItemContainerStyle="{DynamicResource ShellTabItemStyle}"  >
            <TabItem>
                <Grid Margin="2" Width="Auto" Height="Auto">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width=".3*"></ColumnDefinition>
                        <ColumnDefinition Width=".7*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    
                    <ContentControl Grid.Column="0" prism:RegionManager.RegionName="SubFilterRegion"  >
                        
                    </ContentControl>
//View A loads initially into submainregion. when i click on one of the row of the grid, it should load another view in different tab. but along with search panel
                    <ContentControl  Grid.Column="1" prism:RegionManager.RegionName="SubMainRegion" ></ContentControl>
                </Grid>
                
            </TabItem>

        </TabControl>
            <!--</ItemsControl>-->
              

</Window>


Sep 4, 2012 at 7:23 AM

I dont think you will be able to achieve this with this design. You'll have to take the dynamic region and scroped region approach.

Sep 4, 2012 at 7:44 AM

can you shed some more light?

Developer
Sep 4, 2012 at 12:29 PM
Edited Sep 4, 2012 at 12:36 PM

Hi Kavya,

Based on my understanding of the aforementioned scenario, I believe the behavior your are experiencing (the two views are shown in the same tab) could be caused if you are returning the value true in the IsNavigationTarget method of your views / view models, this way if you navigate to the same type of a view in a region more than once, if an instance of this view is present, this will be  navigated instead of creating a new one. Hence, you could try returning false in this method. Take into account that if you do this and several views of the same type are loaded in the region, as the user Ganesh mentioned above, you may have to remove the regions defined in these views when navigating away from them in order to avoid the following exception:

"An exception occurred while creating a region with name 'yourRegion'. The exception was: System.ArgumentException: Region with the given name is already registered: yourRegion"

Also, note that, your views and view models may have to be decorated with the [PartCreationPolicy(CreationPolicy.NonShared)] attribute, as this will cause the container to return a new instance of the view each time you request one.

On the other hand, if this approach doesn't fit the requirements of your scenario, for example if you need to dynamically change the contents of the already loaded regions in these views and removing the regions from the region manager is not an option, then using the scoped regions approach may be the alternative, although by default Prism only supports the use of scoped regions with view injection approach.

You can find more information about this in the following section of the documentation:

Regards,

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

Sep 4, 2012 at 1:11 PM

Hi Agustin, 

 

Thank you for your inputs. my requirement is slightly different. I want 3 views to be loaded every time a open a tab. something like this

 

Tab A

 ---  left region

 --- Tool bar

 --- Main region ( This can contain any Usercontrol. lets say a grid for now) on click of a row, i want Tab B open with same structure. something like below

Tab B 

  ---  left region

 --- Tool bar

 --- Main region (This will have a seperate User control)

Ganesh's inputs helped me lot. but when I put usercontrols into the content control inside tab, I lose the header of the tab as viewmodel of mainregion(content control) is not bound to tab. I think for same reason, when i click on row in Tab A, Tab b opens in same tab.

I cannot have toolbar and left region outside tab control for business requirement reasons. so to summarise, i want to load 3 views in a tab. and if i can bind mainregion's viewmodel anw which way to tab, this would be awesome for me. 

Does this help? Desperate for some help from experts like yourself.

 

Kavya

 

Sep 4, 2012 at 1:16 PM

 

Just another update. I couldn't edit the post. strange.

in isNavigationTarget, I did make it false. all works awesomely well if i've just one viewmodel bound to the tab control.

 see this post's I raised. i've got all this working now.http://compositewpf.codeplex.com/discussions/392910 (thanks for helping me here too)

Sep 5, 2012 at 3:54 PM

I found this solution. 

what I did was instead of above structure, was change the way regions are established

Tab A

 --- Main region (This will have a seperate User control)

                  --    <contentcontrol for left region 

                  --    <contentcontrol for top region 

 

this way, only one vm is bound to the tab and i could load any number of tabs with similar controls

Please ask if you require more details.

thanks for taking time on this thread