views:

122

answers:

2

I am using MVVM. I have a tab control. I will have a collection of items. I want to display each of this item in the collection as a tab item. The view in each tab item is different and may have its own viewmodel. How do I achieve this? E.g. I have 3 items in the collection. The Tab item template contains an ItemControl. I would like to now have 3 Tabs created and the ItemControls inside each tabitem may be showing different views.

One way I could do is have a single view and viewmodel for each item. Now based on some condition the View will display different UI elements and behave differently. But I am afraid this will make the view quite complex over a period of time.

Edit: Goblin's solution below works fine but I have an issue when a custom style applied to TabControl.

<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Template">
  <Setter.Value>
     <ControlTemplate TargetType="TabControl">
      <Grid>
         <Grid.ColumnDefinitions>
            <ColumnDefinition/> <ColumnDefinition />
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
            <RowDefinition Height="Auto" Name="RowDefinition0" />
            <RowDefinition Height="*" Name="RowDefinition1" />
         </Grid.RowDefinitions>
         <TabPanel Grid.Column="0" Grid.Row="0" />
         <Border Grid.Column="0" Grid.Row="1">
             <ContentPresenter Content="{TemplateBinding TabControl.SelectedContent}" ContentTemplate="{TemplateBinding TabControl.SelectedContentTemplate}" ContentStringFormat="{TemplateBinding TabControl.SelectedContentStringFormat}" ContentSource="SelectedContent" Name="PART_SelectedContentHost" Margin="{TemplateBinding Control.Padding}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
          </Border>
          </Grid>
          <ControlTemplate.Triggers>

EDIT: This has been resolved by adding ContentTemplateSelector to the ContentPresenter in the above TabControl style

+4  A: 

Have you tried using DataTemplateSelectors?

Basically, you publish a collection of smaller ViewModels in your main ViewModel - then in the DataTemplateSelector choose your template based on the type of ViewModel?

<UserControl.Resources>
    <DataTemplate x:Key="HeaderTemplate">
        <TextBlock Text="CMR"/>
    </DataTemplate>
    <DataTemplate x:Key="FirstTemplate">
        <local:FirstView/>
    </DataTemplate>
    <DataTemplate x:Key="SecondTemplate">
        <lcoal:SecondView/>
    </DataTemplate>
    <local:TemplateSelector x:Key="TemplateSelector" FirstTypeTemplate="{StaticResource FirstTemplate}" SecondTypeTemplate={StaticResource SecondTemplate}/>
</UserControl.Resources>
<TabControl ItemsSource="{Binding SmallerViewModels}" ContentTemplateSelector="{StaticResource TemplateSelector}" ItemTemplate="{StaticResource HeaderTemplate}">

In Code-behind:

public class TemplateSelector:DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if(item.GetType() == typeof(FirstViewModel)
            return FirstTypeTemplate
        return SecondTypeTemplate;
    }
    public DataTemplate FirstTypeTemplate { get; set; }
    public DataTemplate SecondTypeTemplate { get; set; }
}

EDIT: ViewModels:

public class SharedViewModel
{
    public SharedViewModel()
    {
        SmallerViewModels = new List<ISmallViewModel>();
        SmallerViewModels.Add(new FirstViewModel());
        SmallerViewModels.Add(new SecondViewModel());
    }
    public IList<ISmallViewModel> SmallerViewModels{get;private set;}
}
public interface ISmallViewModel{}
public class FirstViewModel:ISmallViewModel
{
    public string FirstDescription
    {
        get{return "My first ViewModel";}
    }
}
public class SecondViewModel:ISmallViewModel
{
    public string SecondDescription
    {
        get{return "My second ViewModel";}
    }
}

Views

<UserControl  .... x:Class="...FirstView">
    <TextBlock Text="{Binding FirstDescription}"
</UserControl>
<UserControl  .... x:Class="...SecondView">
    <TextBlock Text="{Binding SecondDescription}"
</UserControl>
Goblin
Thanks Goblin. How do I implement this when I have two difference views but same view model i.e. shared viewmodel scenario?
byte
Just insert the view into the Fitting DataTemplate (FirstTemplate vs SecondTemplate.
Goblin
Goblin, I understand what you are saying but being new to Wpf and templating, it would help if you can give a small code example or reference to some article.
byte
Okay - I'll try and elaborate on my example.
Goblin
Thanks Goblin. Would <TabControl ItemsSource="{Binding ViewModelCollection}" become <TabControl ItemsSource="{Binding SmallerViewModels}" in case of SharedViewModel? I wanted some information on the DataContext in this case
byte
You are of course correct. The DataContext of the ItemsControl should be the SharedViewModel. I'll update to reflect this. EDIT: The DataContext would typically be inherited from the main view's datacontext which would be of type SharedViewModel.
Goblin
True. I have similar code. Thank you again. Appreciate that.
byte
Goblin, I have implemented the functionality. But I observed that SelectTemplate never gets called if I style my TabControl. I removed the style and it worked fine. Any clues as to how I can resolve this?
byte
I believe the problem is because I have ContentTemplate in the style and with the MVVM solution is am providing ContentTemplateSelector. This is in conflict.I have edited my question with the Style code. Could you have a look at that please...
byte
You are correct - if you set the Template property via Style - you effectively override the Selector. The Selector only works if you do not set the Template directly. The TabControl is a bit different when it comes to templating - see this post for more detail: http://www.switchonthecode.com/tutorials/the-wpf-tab-control-inside-and-out
Goblin
Thanks, I have seen that url before. In this specific case of using the template selector, how would I style my tab control?
byte
You style the headers as they do in the sample - as for the rest of the TabControl (ie. the actual pages) you just style your FirstView and SecondView - these two control what is shown - and can be styled to your hearts content :).
Goblin
Thanks Goblin. Done that. The TabControl style above is based on a standard style which I found using ShowMeTheTemplate tool. All I did is made changes to the background, border etc colors.
byte
byte
+3  A: 

Create a datatemplate for each view. Implement a DataTemplateSelector class which given an item returns the correct datatemplate. If the collection of items is called Items your xaml would look something like this

<TabControl 
    ItemsSource="{Binding Path=Items}"
    ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
Wallstreet Programmer
Sorry, i didn't comment on your post earlier. I appreciate your reply and have up-voted your reply.
byte