rikoe
rikoe

Reputation: 1699

WPF TabControl - Select different tab when TabItem Visibility changes

I have a TabControl on a UserControl backed by a ViewModel, and the Visibility of one of the tab items is bound to a property on the ViewModel.

<TabControl x:Name="myTabControl">
    <TabItem Header="Tab 1" />
    <TabItem Header="Tab 2" Visibility="{Binding HasData, Converter={StaticResource boolToVisibilityConverter}}"/>
</TabControl>

When the Visibility of the TabItem changes, it collapses (hides) the TabItem header, but it continues displaying its content.

I want the TabControl to switch to the visible tab when the other tab is hidden, and was a little surprised to find out it doesn't happen automatically.

Attaching an event handler to the SelectionChanged event of the TabControl shows that TabItem.IsSelected (and TabControl.SelectedItem) is not even affected when the TabItem.Visibility changes (is this a bug?!).

I've tried both a property trigger:

    <!-- This doesn't compile because of TargetName on the Setter, think you can only use it in Control Templates.
         I don't know how to refer to the parent TabControl from within the TabItem style. -->
    <TabControl.ItemContainerStyle>
        <Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}">
            <Style.Triggers>
                <Trigger Property="Visibility" Value="Collapsed">
                    <Setter TargetName="myTabControl" Property="SelectedIndex" Value="0" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TabControl.ItemContainerStyle>

and a data trigger:

    <!-- This doesn't quite work, it affects the Visibility of the TabItem's content too -->
    <TabControl.Style>
        <Style TargetType="{x:Type TabControl}" BasedOn="{StaticResource {x:Type TabControl}}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=SelectedItem.Visibility, ElementName=tabControl}" 
                             Value="Collapsed">
                    <Setter Property="SelectedIndex" Value="0" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TabControl.Style>

I can't get the triggers to work, and there's no VisibilityChanged event I can handle, so I'm kind of stuck and would appreciate some help.

Upvotes: 11

Views: 8894

Answers (3)

user2220595
user2220595

Reputation: 91

You could add this event handler to the code behind. It will test your control in the first place and on changes to tab visibility due to bindings.

Instead of doing this OnLoaded of course it makes total sense to put this into an attached Property. (AutoSelect?) . The code is the same. You get called in the first place and attach events to IsVisibleChanged. Then the only trick is to use a lambda (Parameter binding) to get the TabControl instance into the event callback. I am posting this solution, because it is shorter.

private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
    var tabControl = (TabControl) sender;
    // register visibility changed to react on changes
    foreach (TabItem item in tabControl.Items)
    {
        item.IsVisibleChanged += (mSender, ev) => item_IsVisibleChanged(mSender, ev, tabControl);
    }
    // if current selected tab is invisible, find and select first visible one.
    if (!((TabItem) tabControl.SelectedItem).IsVisible)
    {
        foreach (TabItem item in tabControl.Items)
        {
            if (item.IsVisible)
            {
                tabControl.SelectedItem = item;
                return;
            }
        }
    }
}

private static void item_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e, TabControl tabControl)
{
    // just became IsVisible = false
    if ((bool)e.NewValue == false)
    {
        if (tabControl == null) return;
        ItemCollection items = tabControl.Items;
        foreach (UIElement item in items)
        {
            if (item.IsVisible)
            {
                tabControl.SelectedItem = item;
                return;
            }
        }
    }
}

Upvotes: 1

Divya Singhal
Divya Singhal

Reputation: 41

Bind SelectedIndex of TabControl to a property. And change the value of this property to the index of tab which you want to display whenever you change the visibility to collapse of tab item.

Upvotes: 3

mdm20
mdm20

Reputation: 4563

The TabItem class has an IsVisibleChanged event that you can use.

Upvotes: 6

Related Questions