In Prism (Composite Application Guidelines), how can I get views dynamically loaded into TabControl?

StackOverflow https://stackoverflow.com/questions/625994

  •  05-07-2019
  •  | 
  •  

Question

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)?

Was it helpful?

Solution

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}" />

OTHER TIPS

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.

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top