3

I am witting a new WPF application using C# with the help of Prism 6. and the MVVM design pattern.

I have a main windows with a Top, Right, and a Center region. On the top region I have a toolbar, when a user clicks "Show Message" button, I show a view called "Message" in the center region. I am able to do that using RegionManager.RequestNavigate method to show the "Message" view which is working fine.

However, my "Message" view have multiple tabs. When the user clicks on the TabControlItems, I want to be able to show different views in the tab-Content.

Here is how my Message view look like. The idea here is to have a collection of ViewModels, and display the tabs according to the collection.

<TabControl ItemsSource="{Binding ViewModelCollection}"
            Background="Transparent">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <prism:InvokeCommandAction Command="{Binding TabSelectionChangedCommand}"
                                       CommandParameter="{Binding ViewName}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding TabTitle}" />
        </DataTemplate>
    </TabControl.ItemTemplate>

    <TabControl.ContentTemplate>
        <DataTemplate>
            <ContentControl prism:RegionManager.RegionName="{x:Static foundation:RegionNames.TabContentRegionName}" />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

Then in my MessageViewModel I have the following code

public class MessageViewModel : BindableBase
{
    public ObservableCollection<TabBasedNavigationAwareViewModel> ViewModelCollection { get; set; }
    public DelegateCommand<string> ChangeTabContent { get; set; }

    public MessageViewModel(IUnitOfWork unitOfWork, IPassportManager passportManager, ICoreRegionManager regionManager, IUnityContainer container)
    : base(unitOfWork, passportManager, regionManager)
    {
        ViewModelCollection = new ObservableCollection<TabBasedNavigationAwareViewModel>();

        ViewModelCollection.Add(container.Resolve<FirstViewModel>());
        ViewModelCollection.Add(container.Resolve<SecondViewModel>());

        ChangeTabContent = new DelegateCommand<string>(HandleChangeContent, CanChangeContent);
    }

    protected bool CanChangeContent(string viewName)
    {
        return true;
    }

    protected void HandleChangeContent(string viewName)
    {
        IRegion region = RegionManager.Regions[RegionNames.TabContentRegionName];

        region.RequestNavigate(new Uri("Modules.Messages.Views." + viewName, UriKind.Relative));
    }

    public override void OnNavigatedTo(NavigationContext navigationContext)
    {
        ChangeTabContent.Execute("FirstView");
    }
}

The problem is when the application starts I get an error

The region manager does not contain the TabContent region.

I clearly understand the error and why it is happening. But not sure how to solve it. May be Regions is the wrong way to do when using tab-controls inside a main region. I also tried to call the following code from the MessageViewModel constructor RegionManager.RegisterViewWithRegion(RegionNames.TabContentRegionName, typeof(MessageView));

What is the correct way to manage/display the correct view when the user click on the tabs?

Please note that I am using Fody.PropertyChanged package so it automatically notify when the property changed.

UPDATED

I tried to remove regions from view, now I get the tabs and the content screen is showing the FirstViewModel full name instead of the corresponding view. Here is my code without regions

public class MessageViewModel : BindableBase
{
    public ObservableCollection<TabBasedNavigationAwareViewModel> ViewModelCollection { get; set; }

    public MessageViewModel(IUnitOfWork unitOfWork, IPassportManager passportManager, ICoreRegionManager regionManager, IUnityContainer container)
    : base(unitOfWork, passportManager, regionManager)
    {
        ViewModelCollection = new ObservableCollection<TabBasedNavigationAwareViewModel>();

        ViewModelCollection.Add(container.Resolve<FirstViewModel>());
        ViewModelCollection.Add(container.Resolve<SecondViewModel>());
    }
}

Here is the view

<TabControl ItemsSource="{Binding ViewModelCollection}"
             Background="Transparent">

    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding TabTitle}" />
        </DataTemplate>
    </TabControl.ItemTemplate>

</TabControl>
Junior
  • 11,602
  • 27
  • 106
  • 212
  • What is the `TabContent` region for? Do you navigate to it somewhere? I guess, you can just remove it. – Haukinger Mar 14 '18 at 09:31
  • But how do I change content when a tab is clicked? – Junior Mar 14 '18 at 14:14
  • That's the job of the `TabControl`, isn't it? You give it a collection of view models and it shows a collection of tabs. – Haukinger Mar 14 '18 at 14:54
  • @Haukinger yes in a normal `TabControl` would handle the content switching for me. But how would it where to place the content in? I removed region from the ViewContent but that gives me a blank screen. I updated my question with the updated code – Junior Mar 14 '18 at 15:13

1 Answers1

0

Also add some DataTemplates for your tab view models to a resource dictionary nearby

<DataTemplate DataType="{x:Type FirstViewModel}">
    <TextBlock Text="{Binding SomeContent}"/>
</DataTemplate>
Haukinger
  • 10,420
  • 2
  • 15
  • 28