Search code examples
wpfvisibilityparentchildren

How to hide parent control while showing child content?


How can I hide a parent TabControl until the child in one of its tabs is clicked on? Obviously, I need the child to be visible for the user to be able to click on it. The only thing that I have managed to come up with so far is a bit of a hack... I'm displaying an extra child item over the top of the TabControl and then hiding it and showing the TabControl upon clicking. Here is my hack:

XAML:

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="MainWindow" Height="500" Width="600" 
        PreviewMouseLeftButtonUp="Grid_PreviewMouseLeftButtonUp">
    <Window.Resources>
        <Style TargetType="{x:Type Rectangle}">
            <Setter Property="Width" Value="300" />
            <Setter Property="Height" Value="250" />
        </Style>
    </Window.Resources>
    <Grid>
        <TabControl Name="TabControl" Width="350" Height="300">
            <TabItem Header="Original">
                <Rectangle Fill="Red" />
            </TabItem>
            <TabItem Header="Modified">
                <Rectangle Fill="Blue" />
            </TabItem>
            <TabControl.Style>
                <Style TargetType="{x:Type TabControl}">
                    <Setter Property="Visibility" Value="Collapsed" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsTabControlVisible}" 
                            Value="True">
                            <Setter Property="Visibility" Value="Visible" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TabControl.Style>
        </TabControl>
        <Rectangle Fill="Red" Margin="0,22,0,0"
            PreviewMouseLeftButtonUp="Rectangle_PreviewMouseLeftButtonUp">
            <Rectangle.Style>
                <Style TargetType="{x:Type Rectangle}" 
                    BasedOn="{StaticResource {x:Type Rectangle}}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsTabControlVisible}" 
                            Value="True">
                            <Setter Property="Visibility" Value="Collapsed" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Rectangle.Style>
        </Rectangle>
    </Grid>
</Window>

Code behind:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        public static readonly DependencyProperty IsTabControlVisibleProperty = 
            DependencyProperty.Register(nameof(IsTabControlVisible), typeof(bool), 
            typeof(MainWindow), null);

        public bool IsTabControlVisible
        {
            get { return (bool)GetValue(IsTabControlVisibleProperty); }
            set { SetValue(IsTabControlVisibleProperty, value); }
        }

        private void Rectangle_PreviewMouseLeftButtonUp(object sender, 
            MouseButtonEventArgs e)
        {
            IsTabControlVisible = true;
        }

        private void Grid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!TabControl.IsMouseOver) IsTabControlVisible = false;
        }
    }
}

I've changed the content to plain Rectangles for simplicity.

How can I improve this situation? I don't like the idea of duplicating the child content to make this work. Does anyone have any better solutions?


Solution

  • You could, as @XAMIMAX mentioned, hide the TabControl but not by its Opacity (because setting it to 0 would hide the child too). The following XAMLis just a quick & dirty proof of concept by hiding all the Elements except the Child:

    <TabControl Name="TabControl" Width="350" Height="300" BorderBrush="Transparent">
        <!-- Content of the Tabcontrol -->
        <TabItem Header="Original">
            <Rectangle Fill="Red" />
        </TabItem>
        <TabItem Header="Modified" >
            <Rectangle Fill="Blue" />
        </TabItem>
    
        <!-- Triggers for the TabControl -->
        <TabControl.Triggers>
            <!-- Set Borderbrush to Black when tab is clicked -->
            <EventTrigger RoutedEvent="MouseDown">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)">
                                <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Colors.Black}"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
            <!-- Reset BorderBrush when Mouse leaves the TabControl (choose wathever condition you like to hide the tabs) -->
            <EventTrigger RoutedEvent="MouseLeave">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)">
                                <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Colors.Transparent}"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </TabControl.Triggers>
    
        <!-- Set "Style" of Container -->
        <TabControl.ItemContainerStyle>
            <Style TargetType="{x:Type TabItem}">
                <!-- Hide Header -->
                <Setter Property="Visibility" Value="Hidden"/>
                <!-- Show header when Border of parent is Black (you can choose a different Property if you like) -->
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabControl}}, Path=(BorderBrush).(SolidColorBrush.Color)}" Value="Black">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TabControl.ItemContainerStyle>
    </TabControl>
    

    The Tabcontrol has 2 Triggers: Setting the BorderBrush to Black when someone clicks on the TabControl and setting it to Transparent when the MousePointer leaves the TabControl. Theese two handle the visibility for the TabControl itself. Note: Add additional Triggers if your Background is different from the Color behind the TabControl.

    The TabItem has a Trigger bound to the Color of the TabControl Border. If the Color is black, show the Header of the TabItem otherwise, hide them.

    enter image description here