My next step in exploring/learning WPF/MVVM brought me to a MainWindow with a TabControl and four TabItems. Each tab has the same content, a set of four CheckBoxes defined in one View and consequently in one ViewModel. As I can see from various Debug.WriteLine statements in various constructors the one View is instantiated four times as well the ViewModel four times.
So far so good, but the data of each tab should be stored in a backend (yet to be determined) so I need to inform the View instances and consequently the ViewModel instances to which TagItem they belong. So that the checked CheckBoxes of each TabItem can be stored separately.
I did try to use the ObjectDataProvider text to pass an argument to the OneView constructor but could not get it working. Any suggestions?
<Window x:Class="FourTabsOneView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vws="clr-namespace:FourTabsOneView.Views"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="400">
<TabControl x:Name="tControl">
<TabItem x:Name="One" Header="TabOne" Width="80">
<vws:OneView/>
</TabItem>
<TabItem x:Name="Two" Header="TabTwo" Width="80">
<vws:OneView/>
</TabItem>
<TabItem x:Name="Three" Header="TabThree" Width="80">
<vws:OneView/>
</TabItem>
<TabItem x:Name="Four" Header="TabFour" Width="80">
<vws:OneView/>
</TabItem>
</TabControl>
</Window>
<UserControl x:Class="FourTabsOneView.Views.OneView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vms="clr-namespace:FourTabsOneView.ViewModels"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="400">
<UserControl.DataContext>
<vms:OneViewModel />
</UserControl.DataContext>
<StackPanel Orientation="Vertical">
<CheckBox x:Name="cb01" IsThreeState="False" Height="30"
IsChecked="{Binding CB01}"
Content="CheckBox A" />
<CheckBox x:Name="cb02" IsThreeState="False" Height="30"
IsChecked="{Binding CB02}"
Content="CheckBox B" />
<CheckBox x:Name="cb03" IsThreeState="False" Height="30"
IsChecked="{Binding CB03}"
Content="CheckBox C" />
<CheckBox x:Name="cb04" IsThreeState="False" Height="30"
IsChecked="{Binding CB04}"
Content="CheckBox D" />
</StackPanel>
</UserControl>
The idiomatic way to do this is by creating an observable collection in your view model that holds each of the children's view models you want, and then binding the collection to the TabControl
's ItemsSource
property. That will populate the tab control with one tab for each of your elements in the collection.
The rest of the story is simple, just templates. You have your ContentTemplate
property which defines how the children should look (your user control) and your ItemTemplate
which defines how the header should look (a text block bound to a Name
property in your childrens' VM).
Also don't try to manually change your user control's data context, it's already handled by the framework to be correct.