Handling WPF TabItems Visibility property

aioracle picture aioracle · Mar 4, 2013 · Viewed 12.2k times · Source

I have been reading about Visibility.Collapsed for TabItems. When the Visibility is set to Collapsed, the TabItem header is hidden but the contents are still visible.

I have also tried the following approch mentioned in here, but no luck.

Is there any way to get the contents inside the TabItems to hide and also select the tab that is visible.

Answer

Federico Berasategui picture Federico Berasategui · Mar 4, 2013

You don't need any of that. Conceptually, a TabControl is just a graphical representation of an ObservableCollection<ViewModel>, where each viewmodel is represented by a tab item, and there's only 1 SelectedItem at a given time:

<Window x:Class="WpfApplication4.Window12"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window12" Height="300" Width="300">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
    </Window.Resources>
        <TabControl ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
                    <Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}"/>
                    <Setter Property="Header" Value="{Binding Title}"/>
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>
</Window>

Code Behind:

using System.Windows;
using BaseFramework.MVVM;
using System.Collections.ObjectModel;

namespace WpfApplication4
{
    public partial class Window12 : Window
    {
        public Window12()
        {
            InitializeComponent();
            DataContext = new TabbedViewModel()
                          {
                              Items =
                                  {
                                      new TabViewModel() {Title = "Tab #1", IsEnabled = true, IsVisible = true},
                                      new TabViewModel() {Title = "Tab #2", IsEnabled = false, IsVisible = true},
                                      new TabViewModel() {Title = "Tab #3", IsEnabled = true, IsVisible = false},
                                  }
                          };
        }
    }

ViewModel:

    public class TabbedViewModel: ViewModelBase
    {
        private ObservableCollection<TabViewModel> _items;
        public ObservableCollection<TabViewModel> Items
        {
            get { return _items ?? (_items = new ObservableCollection<TabViewModel>()); }
        }

        private ViewModelBase _selectedItem;
        public ViewModelBase SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                NotifyPropertyChange(() => SelectedItem);
            }
        }
    }

    public class TabViewModel: ViewModelBase
    {
        private string _title;
        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                NotifyPropertyChange(() => Title);
            }
        }

        private bool _isEnabled;
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                NotifyPropertyChange(() => IsEnabled);
            }
        }

        private bool _isVisible;
        public bool IsVisible
        {
            get { return _isVisible; }
            set
            {
                _isVisible = value;
                NotifyPropertyChange(() => IsVisible);
            }
        }
    }
}

Then, its just a matter of inheriting TabViewModel for each one of your tabs (creating the appropiate logic inside of each), and a proper DataTemplate for each one of these derived classes in the app.xaml or something.

Whenever you want to remove a tab item from the view, instead of manipulating the view you manipulate the ViewModel. this is the WPF approach for everything. It simplifies everything by removing the need to manipulate complex objects (UI elements) in code. Whenever you set

TabbedViewModel.SelectedItem.IsVisible = false;, make sure you also do:

TabbedViewModel.SelectedItem = TabbedViewModel.Items.First(x => x.IsVisible && x.IsEnabled);

This will prevent you from ever falling into the case of having an invisible tab item as the selected item.