views:

2384

answers:

4

In a Prism v2 application, I define two regions, each a tabitem in a tabcontrol:

<UniformGrid Margin="10">
    <TabControl>
        <TabItem Header="First" Name="MainRegion" cal:RegionManager.RegionName="MainRegion"/>
        <TabItem Header="Second" Name="SecondRegion" cal:RegionManager.RegionName="SecondRegion"/>
    </TabControl>
</UniformGrid>

In the bootstrapper two modules are loaded and each injects a view into each of the tabitems:

protected override IModuleCatalog GetModuleCatalog()
{
    ModuleCatalog catalog = new ModuleCatalog();
    catalog.AddModule(typeof(SecondModule.SecondModule));
    catalog.AddModule(typeof(HelloWorldModule.HelloWorldModule));
    return catalog;
}

Now, of course, I want to perform the decoupling magic that I keep reading about and uncomment one of the modules and see its tab item not appear at all. Instead, on the contrary, there are still two TabItems and one is empty. This tells me that my application is still tightly coupling data and UI as in the bad old WinForm days.

So what do I need to do here to make this dynamic, so that the UI changes dynamically based on what modules are loaded, i.e. so that I could load 10 modules/views in my bootstrapper and there would automatically be 10 TabItems in the TabControl?

INTERMEDIATE ANSWER:

If I just make one region in a TabControl:

<TabControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion"/>

and then load both controls into the MainRegion:

        public void Initialize()
        {
            regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.SecondView));
        }
...
        public void Initialize()
        {
            regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.HelloWorldView));
        }

then I get a TabControl with two tabs, each with a view in it, which is what I want.

But the TabItem headers are not defined. How do I dynamically define the header (e.g. not in the XAML but dynamically back in the View classes)?

A: 

You are on the right track with your modification.

The way I usually achieve the header is by adding an object to the region instead of a control, and datatemplating it with the control.

This object defines a property (let's say MyHeaderProperty) which I then use to bind to using an ItemContainerStyle on the TabControl.

I do not know if there is a way to achieve that without resorting to that kind of trick (an intermediate object and a DataTemplate).

Denis Troller
+6  A: 

This works too:

public class View : UserControl
{

    public string ViewName { get; set; }

}

and then in the shell:

<Window.Resources>        
       <Style TargetType="{x:Type TabItem}" x:Key="TabItemRegionStyle">
                <Setter Property="Header" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.ViewName}" />
       </Style>
</Window.Resources>
    ...
<TabControl cal:RegionManager.RegionName="RightRegion" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" Grid.Column="2" 
          x:Name="RightRegion" ItemContainerStyle="{StaticResource TabItemRegionStyle}" />
Well done... been looking for this for a while!
Anderson Imes
+4  A: 

Nice.

You can remove the ViwewName property on the view and change the binding on the TabItem value to be Value="{Binding DataContext.HeaderInfo}" ... where HeaderInfo is a property of your DataContext object - IE the business object which the Tab Item represents. This is a little more elegant.

Pete Maher
A: 

I am trying the same thing in Silverlight and Prism, when i set the UserControl.Resources i am getting the following error.

Invalid attribute value {x:Type TabItem} for property TargetType. [Line: 11 Position: 27

Does any one know how to fix this. Help will be appriciated.

Anwar