WPF: hiding a tab item in a tab control thats bound to an observable collection

11,736

Solution 1

If you can modify your vm:TabViewModel I should change your IsVisible to a Visibility property and use the following ContentTemplate:

<TabControl.ContentTemplate>
    <DataTemplate DataType="{x:Type vm:TabViewModel}">
        <c:MyTabItem Visibility={Binding Visibility}/>
    </DataTemplate>
</TabControl.ContentTemplate>

Else you could use a converter to change the boolean IsVisible to an Visibility enum:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows;

namespace Something.Converters
{
    [ValueConversion(typeof(bool), typeof(Visibility))]
    public class BoolToVisibilityConverter : IValueConverter
    {

        #region IValueConverter Members
        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is bool && targetType == typeof(Visibility))
            {
                bool val = (bool)value;
                if (val)
                    return Visibility.Visible;
                else
                    if (parameter != null && parameter is Visibility )
                        return parameter;
                    else
                        return Visibility.Collapsed;
            }
            throw new ArgumentException("Invalid argument/return type. Expected argument: bool and return type: Visibility");
        }

        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value that is produced by the binding target.</param>
        /// <param name="targetType">The type to convert to.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Visibility && targetType == typeof(bool))
            {
                Visibility val = (Visibility)value;
                if (val == Visibility.Visible)
                    return true;
                else
                    return false;
            }
            throw new ArgumentException("Invalid argument/return type. Expected argument: Visibility and return type: bool");
        }
        #endregion
    }
}

Inculde the namespace in your xaml (your root element, Window in this example):

<Window xmlns:converters="clr-namespace:Something.Converters"
.../>

And in your resources:

<Window.Resources>
    <converters:BoolToVisibilityConverter x:Key="boolToVisibilityConverter"/>
</Window.Resources>

And finaly the binding:

<TabControl.ContentTemplate>
    <DataTemplate DataType="{x:Type vm:TabViewModel}">
        <c:MyTabItem Visibility={Binding IsVisible, Converter={StaticResource boolToVisibilityConverter}, ConverterParameter=Visibility.Collapsed}/>
    </DataTemplate>
</TabControl.ContentTemplate>

I think thats it :)

Edit: Ow and change the ConverterParameter to Visibility.Collapsed to Visibility.Hidden for hidden ;)

Solution 2

Got the correct answer with the help of this answer

<TabControl.ItemContainerStyle>
  <Style TargetType="{x:Type TabItem}">
    <Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource boolToVisibilityConverter}"/>
  </Style>
</TabControl.ItemContainerStyle>

Use the System.Windows.Controls.BooleanToVisibilityConverter for conversion from bool to Visibilty.

Scott's suggestion of using CollectionView is also promising.

Solution 3

I suggest using a CollectionView. This is kind of like an abstract view of a collection where you can see a filtered portion of it. By binding to the CollectionView rather than the collection itself, you should be able to only see the ones you want, and the collection is still there in the background.

Share:
11,736
bluebit
Author by

bluebit

Updated on June 04, 2022

Comments

  • bluebit
    bluebit almost 2 years

    I have a tab control bound to an observablecollection for dynamic tabs as follows:

    <TabControl ItemsSource="{Binding AllTabs}" SelectedIndex="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            <TabControl.ItemTemplate>
                <DataTemplate>
                       <!--.............. -->
                </DataTemplate>
            </TabControl.ItemTemplate>
    
            <TabControl.ContentTemplate>
                <DataTemplate DataType="{x:Type vm:TabViewModel}">
                    <c:MyTabItem/>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    

    So, the tab headers and contents are defined dynamically and assigned as the observable collection changes. Now, I would like to hide some tabs without deleting them in the collection behind - in order to keep the data should the tab reopen.

    Ideally, each chat tab viewmodel has a IsVisible property that is set to true by default. However, where do I bind such a property to in order to make a tab item collapse?