8

My WPF Windows contains a TabControl which displays content on different tabs. A click on the button below executes a method via ICommand interface / Binding. The called method generates text which is intended to be displayed in the second tab.

Application Mockup

How can I switch to the second tab on button click without violating the MVVM Pattern?

I tried to bind the TabItem.IsSelected Property to something in my ViewModel but I wanted to use the other tabs (tab1) as well.

Any thoughts?

Joel
  • 4,862
  • 7
  • 46
  • 71

5 Answers5

15

I found it out by myself.

The key is a two way binding. When the button is clicked it sets the property DisplayXamlTab true. The IsSelected attribute is bound to this variable. if another tab is clicked the binding will set the DisplayXamlTab Property to false.

Note: UpdateSourceTrigger=PropertyChanged is also very important

Code comes below:

XAML:

        <TabItem Header="XAML" IsSelected="{Binding DisplayXamlTab, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            <Grid Background="#FFE5E5E5">
                <TextBox x:Name="TxtXamlOutput" IsReadOnly="True" Text="{Binding XamlText, Mode=TwoWay, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
            </Grid>
        </TabItem>

C# Property:

private bool displayXamlTab;
public bool DisplayXamlTab
{
    get { return this.displayXamlTab; }
    set
    {
        this.displayXamlTab = value;
        this.RaisePropertyChanged("DisplayXamlTab");
    }
}
Joel
  • 4,862
  • 7
  • 46
  • 71
12

if you're going the MVVM way you're going to create two dependency properties in the code behind:

  • ObservableCollection<ItemType> Items;
  • ItemType MySelectedItem;

Then, bind the TabControl ItemsSource property to the Items and bind the SelectedItem property to MySelectedItem

    <TabControl ItemsSource="{Binding Items}"
        SelectedItem="{Binding MySelectedItem, Mode=TwoWay}">
<TabControl.ItemTemplate>
    <DataTemplate>
        <... here goes the UI to display ItemType ... >
    </DataTemplate>
  </TabControl.ItemTemplate>
</TabControl>

When you want to change the selected tab, simply update the MySelectedItem dependecy property

sim1
  • 722
  • 7
  • 12
2

Although this question is fairly old and well answered already, I thought I'd add this additional answer to demonstrate an alternative way of changing the selected TabItem in a TabControl. If you have a view model for each TabItem, then it can be helpful to have an IsSelected property in it to determine whether it is selected or not. It is possible to data bind this IsSelected property with the TabItem.IsSelected property using the ItemContainerStyle property:

<TabControl ItemsSource="{Binding MenuItems}" TabStripPlacement="Top">
    <TabControl.ItemTemplate>
        <DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}"> 
            <StackPanel Orientation="Horizontal">
                <Image Source="{Binding ImageSource}" Margin="0,0,10,0" />
                <TextBlock Text="{Binding HeaderText}" FontSize="16" />
            </StackPanel>
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}">
            <ContentControl Content="{Binding ViewModel}" />
        </DataTemplate>
    </TabControl.ContentTemplate>
    <TabControl.ItemContainerStyle>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

You can now change the selected TabItem from the parent view model like this:

MenuItems[0].IsSelected = true;

Note that because this property is data bound to the TabItem.IsSelected property, calling this...:

MenuItems[1].IsSelected = true;

... will infact also automatically set the MenuItems[0].IsSelected property to false. so if the view model that you are working with has its IsSelected property set to true, then you can be sure that its related view is selected in the TabControl.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
1

You can create a binding between the view model and the TabControl.SelectedIndex property - i.e., 0 selects the first TabItem , 1 selects the second, etc.

<TabControl DataContext="..." SelectedIndex="{Binding SomeVmProperty}" ...

(alternatively, depending on how you've got things set up, you could bind against SelectedItem...)

JerKimball
  • 16,584
  • 3
  • 43
  • 55
0

You'll likely want to use some sort of "Event Aggregator" pattern (I.e. the Messenger class in MVVM Light) to broadcast some sort of "navigation" message. Your View - the TabControl - can listen for the specific message, and navigate to Tab2 when the message is received.

Alternatively, you can bind the "SelectedItem" property of the TabControl to your ViewModel, and simply call CurrentTab = MySecondTabViewModel from within your VM. This is the approach recommended by @HighPoint in the comments to the OP, but I'm not a fan; see below. Another caveat to this approach is that you need to be familiar with DataTemplates, as you will need to map a view to each ViewModel which you display.

I personally like the first approach, because I don't consider it to be a "responsibility" of the ViewModel to handle tab navigation. If you simply alert your View when data changes in your ViewModel, you allow the View to decide whether or not it wants to change tabs.

Community
  • 1
  • 1
BTownTKD
  • 7,911
  • 2
  • 31
  • 47
  • That's not necessary and adds unneeded overhead and reduces maintainability. Please look at the link in my comment. – Federico Berasategui Mar 06 '13 at 16:54
  • @HighCore - see my comments above. I'm not a fan of using the "SelectedItem" binding. – BTownTKD Mar 06 '13 at 16:59
  • I disagree. But you have a valid point. As an aside, DataTemplates is a must for any WPF developer. From other, more abstract point of view, you can think that setting the "active widget" IS actually a responsibility of the VM. – Federico Berasategui Mar 06 '13 at 17:01
  • I suppose context is a major factor. I've used the SelectedItem binding numerous times, and felt it was appropriate. I've used the Event Aggregator multiple times and also felt it was appropriate ;) – BTownTKD Mar 06 '13 at 17:03