1
votes

I don't want to put it all in one ViewModel, I want to have one viewModel per tabItem.

I created a mainViewModel for the window containing the TabControl, where I have this property currentViewModel,pointed to a default value on the mainViewModel constructor

    public MainViewModel()
    {
        currentViewModel = "viewModel1";
    }

When a user clicks on another tabItem this executes

currentViewModel = "viewModel2";

and of course the set accessor has the onPropertyChanged method

    public String currentViewModel
    {
        get { return _currentViewModel; }

        set
        { 
            _currentViewModel = value;
            OnPropertyChanged("currentViewModel");
        }
    }

Another two viewModels (viewModel1,viewModel2) each one determines the functionality of one of the tabItems I want to switch between.

Now on my Main.xaml I want to bind my dataContext to firstly the MainViewModel and then to the currentViewModel property. so that whenever user clicks on a tabItem the currentViewModel propert get updated and the dataContext point to the corresponding viewmodel.

2
Using a string to set viewmodels? Yuck, i do not think that is necessary. Also one would normally have an if (_fieldOfProperty != value) block around those two lines in the setter.H.B.

2 Answers

3
votes

I think a good approach would be creating usercontrols for each tab item. Then, within each usercontrol you would be referencing the correct viewModel namespace to bind to.

Your MainWindow would have the tabcontrol and each tabitem inside the tabcontrol will be bound to a specific usercontrol(which is treated like a separate View).

mainwindow_View.xaml will be bound to mainwindow_ViewModel.cs tabItem1_View.xaml will be bound to ViewModel1.cs tabItem2_View.xaml will be bound to ViewModel2.cs

If you need a code example, let me know.

3
votes

I doubt that this is what you really want to do, if you have different ViewModels you can still bind your control to a collection of those ViewModels and template them as necessary.

You just need to create different DataTemplates at different level, here is an example (which directly uses models but that should not matter right now):

<TabControl>
    <TabControl.ItemsSource>
        <!-- This is just sample data, normally you would bind this to an
             ObservableCollection<ViewModelBase> or something similar -->
        <x:Array Type="{x:Type sys:Object}">
            <local:Employee Name="John" Occupation="Programmer"/>
            <local:Employee Name="Steve" Occupation="Coffee Getter"/>
            <local:Machine Manufacturer="iCorp" Model="iMachine"/>
            <local:Machine Manufacturer="iCorp" Model="iMachine G2"/>
            <local:Employee Name="Marc" Occupation="GUI Designer"/>
        </x:Array>
    </TabControl.ItemsSource>
    <TabControl.Resources>
        <!-- These datatemplates define the tab-header appearance, by placing them
             in the TabControl.Resources and setting the DataType they get applied
             automatically, just make one light-weight template for each ViewModel -->
        <DataTemplate DataType="{x:Type local:Employee}">
            <TextBlock>
                <Run Text="{Binding Name}"/>
                <Run Text="("/>
                <Run Text="{Binding Occupation}"/>
                <Run Text=")"/>
            </TextBlock>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Machine}">
            <TextBlock>
                <Run Text="{Binding Manufacturer}"/>
                <Run Text=" - "/>
                <Run Text="{Binding Model}"/>
            </TextBlock>
        </DataTemplate>

        <ContentControl x:Key="MainContent" Content="{Binding}">
            <ContentControl.Resources>
                <!-- This represents the content of the TabItems, you probably
                     do not need to create DataTemplates but since you could
                     use a View of the ViewModels instead -->
                <DataTemplate DataType="{x:Type local:Employee}">
                    <StackPanel>
                        <TextBlock Text="{Binding Name}"/>
                        <TextBlock Text="{Binding Occupation}"/>
                        <TextBlock Text="{Binding Id}"/>
                        <TextBlock Text="{Binding IsActive}"/>
                    </StackPanel>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:Machine}">
                    <StackPanel>
                        <TextBlock Text="{Binding Manufacturer}"/>
                        <TextBlock Text="{Binding Model}"/>
                        <TextBlock Text="{Binding VintageYear}"/>
                        <TextBlock Text="{Binding Price}"/>
                    </StackPanel>
                </DataTemplate>
            </ContentControl.Resources>
        </ContentControl>
    </TabControl.Resources>
    <TabControl.ContentTemplate> <!-- Setting the content to the resource -->
        <DataTemplate>
            <Border>
                <StaticResource ResourceKey="MainContent"/>
            </Border>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

I might try to get the specifics for MVVM implemented but this hopefully already gives an idea of how it can be approached.

You might want to set up a template selector (TabControl.ContentTemplateSelector) to get the right views for your ViewModels.