WPF binding different UserControls in a DataTemplate of TabControl
Asked Answered
B

1

2

As a new in WPF and MVVM light, I am struggling to apply the MVVM pattern in a TabControl. I will give you an example of what I am trying to achieve.

TabOne xaml and its view model

<UserControl x:Class="TestTabControl.TabOne"
             xmlns:local="clr-namespace:TestTabControl"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock Text="tab one ..." FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</UserControl>

//TabOne ViewModel
class TabOne : ViewModelBase
{
    public string TabName
    {
        get
        {
            return "TabOne";
        }
    }
}

TabTwo xaml and its viewmodel

<UserControl x:Class="TestTabControl.TabTwo"
             xmlns:local="clr-namespace:TestTabControl"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock Text="tab two ..." FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</UserControl>

//TabTwo ViewModel
class TabTwo : ViewModelBase
{
    public string TabName
    {
        get
        {
            return "TabTwo";
        }
    }
}

and finally the MainWindow xaml and its viewmodel

<Window x:Class="TestTabControl.MainWindow"
        xmlns:local="clr-namespace:TestTabControl"
        mc:Ignorable="d"
        Title="Test Tab Control" MinWidth="500" Width="1000" Height="800">
    <TabControl ItemsSource="{Binding TabViewModels}" >
        <TabControl.ItemTemplate >
            <!-- header template -->
            <DataTemplate>
                <TextBlock Text="{Binding TabName}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                ?????????
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Window>


//MainWindow ViewModel 
class MainWindowViewModel : ViewModelBase
{
    private ObservableCollection<ViewModelBase> _tabViewModels;

    public MainWindowViewModel()
    {
        _tabViewModels = new ObservableCollection<ViewModelBase>();
        TabViewModels.Add(new TabOne());
        TabViewModels.Add(new TabTwo());
    }

    public ObservableCollection<ViewModelBase> TabViewModels
    {
        get
        {
            return _tabViewModels;
        }
        set  // is that part right?
        {
            _tabViewModels = value;
            RaisePropertyChanged(() => TabViewModels);
        }
    }
}

What am I supposed to write in the DataTemplate? Can I pass both usercontrols for TabOne and TabTwo in this DataTemplate in order to get the view for each tab I click? Or do I need to write another DataTemplate?

Bavaria answered 14/4, 2017 at 10:55 Comment(8)
Possible duplicate of How can I bind a List collection to TabControl headers in WPF?Tangible
@AlexPaven that didn't solve my problem. It's not the same issue! ...I changed my question.Bavaria
The question I referenced gives ample details but ok, maybe I didn't understand what exactly the issue is. What are you trying to accomplish exactly? What's the property that's supposed to be the contents of the tab control? If you had another property on each tab viewmodel, you could bind that as the content.Tangible
the tab headers are updated correctly when I run the app. But I don't know how to update the content of the tab too. How can I bind the xaml of each usercontrol in the tab content?Bavaria
It all depends on what you're trying to do. The MVVM way would be to have another property on each tab viewmodel and bind that in the ContentTemplate. If it's a complex property, you can have a DataTemplate specific for the type of the property, but there are many possibilities.Tangible
You can even just have in your resources data templates specific to TabOne and TabTwo (with DataType) and I think the control will pick them up automatically as the content templates. Then the templates themselves behave sort of like lightweight user controls.Tangible
@AlexPaven thanks for the answer, but I still don't know how to implement that. That's why I asked. Can you please give me a code example?Bavaria
See this property? msdn.microsoft.com/en-us/library/… you can associate a DataTemplate to a type. When a content control (like the TabControl) is bound to an object (ItemsSource's contents, in this case), it looks for a DataTemplate that matches the object's type. So, to keep things easy, each tab page should be bound to an object of a different type (as required) and each corresponding UserControl should be placed in a DataTemplate (<TabControl.Resources>...) tagged with the apporopriate {x:Type ...}Accept
L
3

You may already knew the answer by now. But for the benefits of other people, what you need to do is:

    <Grid Margin="10">
      <Grid.Resources>
            <DataTemplate DataType="{x:Type local:TabOne}">
               <local:UserControlOne/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:TabTwo}">
               <local:UserControlTwo/>
            </DataTemplate>
      </Grid.Resources>
      <TabControl Margin="10" 
         ItemsSource="{Binding TabViewModels}">
      </TabControl>
   </Grid>

Please note that, your UserControl for TabOne ViewModel is also named TabOne. I changed it to UserControlOne. Same applies to UserControlTwo.

Locksmith answered 22/2, 2018 at 4:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.