Search code examples
wpftriggerstabcontrolribbon

Hide/Show Grid inside TabControl based on IsChecked of a child


I'm trying to make my own TabControl, here is some part of my Style:

<Style TargetType="TabControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>

                    <TabPanel Grid.Row="0" Panel.ZIndex="1" Margin="0,0,4,-1" IsItemsHost="True" Background="Transparent" />

                    <Border Name="ContentBorder" Grid.Row="1" BorderBrush="#FFD4D4D4" BorderThickness="1" Visibility="{Binding IsChecked, ElementName=HideGrid, Converter={StaticResource Bool2VisibilityConverter}}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="100*"/>
                                    <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <ContentPresenter ContentSource="SelectedContent" />
                        </Grid>
                    </Border>

                    <ToggleButton IsChecked="True" Name="HideGrid" Content="Hide" Grid.Row="1" Margin="722,73,0,0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Visibility="{Binding IsChecked, ElementName=HideGrid, Converter={StaticResource Bool2VisibilityConverter}}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="TabItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TabItem">
                <Grid Name="Item">
                    <Border Name="ItemBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0">
                        <ContentPresenter x:Name="ContentSite"
                           VerticalAlignment="Center"
                            HorizontalAlignment="Center"
                            ContentSource="Header"
                            Margin="10,3"/>
                     </Border>
                </Grid>
                <ControlTemplate.Triggers>
                   <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="ItemBorder" Property="BorderBrush" Value="#FFD4D4D4" />
                        <Setter TargetName="ItemBorder" Property="Background" Value="White"/>
                        <Setter Property="Foreground" Value="#FF2B579A"/>
                    </Trigger>
                    <Trigger Property="IsSelected" Value="False">
                        <Setter TargetName="ItemBorder" Property="BorderThickness" Value="0,0,0,1"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Foreground" Value="#FF2B579A"/>                        </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>

    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="OverridesDefaultStyle" Value="True"/>
</Style>

This style (for now), looks like: Ignore the style of the ToggleButton :)

Ignore the style of the ToggleButton

Basically, what I'm trying to do is to mimic the Office/Windows 8 ribbon:

  1. By clicking in the Button, unselect any tab, hide the grid and the ToggleButton.
  2. By clicking in the TabPanel, show the grid and the Button.

What I'm able to do:

  1. Hide the grid and the ToggleButton by clicking in the ToggleButton.

GIF!

Question:

How can I check the ToggleButton after selecting one TabItem?
How can I unselect all TabItems after checking the ToggleButton?

Working Solution

I just needed to create a custom TabControl class:

public class HideableTabControl : TabControl
{
    #region Variables

    private Button _button;
    private TabPanel _tabPanel;
    private Border _border;

    #endregion

    static HideableTabControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(HideableTabControl), new FrameworkPropertyMetadata(typeof(HideableTabControl)));
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _button = Template.FindName("HideGrid", this) as Button;
        _tabPanel = Template.FindName("TabPanel", this) as TabPanel;
        _border = Template.FindName("ContentBorder", this) as Border;

        if (_button != null)
            _button.PreviewMouseDown += Button_Clicked;

        if (_tabPanel != null)
            foreach (TabItem tabItem in _tabPanel.Children)
            {
                tabItem.PreviewMouseDown += TabItem_PreviewMouseDown;
            }
    }

    private void TabItem_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        var selected = sender as TabItem;

        if (selected != null)
            selected.IsSelected = true;

        _button.Visibility = Visibility.Visible;
        _border.Visibility = Visibility.Visible;
    }

    private void Button_Clicked(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        foreach (TabItem child in _tabPanel.Children)
        {
            child.IsSelected = false;
        }

        _border.Visibility = Visibility.Collapsed;
        _button.Visibility = Visibility.Collapsed;
    }
}

With this simple style:

<Style TargetType="{x:Type local:HideableTabControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:HideableTabControl}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <TabPanel Name="TabPanel"  Grid.Row="0" Panel.ZIndex="1" Margin="5,0,0,-1" IsItemsHost="True" Background="Transparent"/>
                    <Border Name="ContentBorder" Grid.Row="1" BorderBrush="#FFD4D4D4" BorderThickness="0,1" >
                        <ContentPresenter ContentSource="SelectedContent"/> 
                    </Border>
                    <Button Name="HideGrid" Content="^ " Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Style="{DynamicResource OfficeButtonStyle}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Here is the result:

I Did It!


Solution

  • I tried to find a solution using the MVVM pattern

    Try the following:

    View :

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:wpfApplication1="clr-namespace:WpfApplication1"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
            <wpfApplication1:ViewModel/>
        </Window.DataContext>
    
        <Window.Resources>
    
            <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
            <Style TargetType="TabControl">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TabControl}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
    
                                <TabPanel Grid.Row="0" Panel.ZIndex="1" Margin="0,0,4,-1" IsItemsHost="True" Background="Transparent" />
    
                                <Border Name="ContentBorder" Grid.Row="1" BorderBrush="#FFD4D4D4" BorderThickness="1" Visibility="{Binding IsChecked, ElementName=HideGrid, Converter={StaticResource BooleanToVisibilityConverter}}">
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="100*"/>
                                            <ColumnDefinition Width="Auto"/>
                                        </Grid.ColumnDefinitions>
                                        <ContentPresenter ContentSource="SelectedContent" />
                                    </Grid>
                                </Border>
    
                                <ToggleButton 
                                    Command="{Binding Path=UnselectAllTabsCommand}"
                                    IsChecked="{Binding Path=IsHideButtonChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Name="HideGrid" Content="Hide" Grid.Row="1" Margin="722,73,0,0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Visibility="{Binding Path=IsHideButtonChecked, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    
            <Style TargetType="TabItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TabItem">
                            <Grid Name="Item">
                                <Border Name="ItemBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0">
                                    <ContentPresenter x:Name="ContentSite"
                               VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                ContentSource="Header"
                                Margin="10,3"/>
                                </Border>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter TargetName="ItemBorder" Property="BorderBrush" Value="#FFD4D4D4" />
                                    <Setter TargetName="ItemBorder" Property="Background" Value="White"/>
                                    <Setter Property="Foreground" Value="#FF2B579A"/>
                                </Trigger>
                                <Trigger Property="IsSelected" Value="False">
                                    <Setter TargetName="ItemBorder" Property="BorderThickness" Value="0,0,0,1"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Foreground" Value="#FF2B579A"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
    
                <Setter Property="BorderBrush" Value="Transparent"/>
                <Setter Property="OverridesDefaultStyle" Value="True"/>
            </Style>
    
        </Window.Resources>
    
        <Grid>
            <TabControl SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding Path=ShowHideButtonCommand}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <TabControl.Items>
                    <TabItem Header="Tab 1"  />
                    <TabItem Header="Tab 2" />
                    <TabItem Header="Tab 3" />
                </TabControl.Items>
            </TabControl>
        </Grid>
    </Window>
    

    ViewModel:

    namespace WpfApplication1
    {
        using System;
        using System.ComponentModel;
        using System.Runtime.CompilerServices;
        using System.Windows.Input;
    
        /// <summary>
        ///     The view model.
        /// </summary>
        internal class ViewModel : INotifyPropertyChanged
        {
            #region Fields
    
            /// <summary>
            ///     Check if is hide button checked
            /// </summary>
            private bool isHideButtonChecked = true;
    
            /// <summary>
            ///     The selected item
            /// </summary>
            private object selectedItem;
    
            #endregion
    
            #region Public Events
    
            /// <summary>
            ///     The property changed.
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;
    
            #endregion
    
            #region Public Properties
    
            /// <summary>
            ///     Gets or sets a value indicating whether this instance is hide button checked.
            /// </summary>
            /// <value>
            ///     <c>true</c> if this instance is hide button checked; otherwise, <c>false</c>.
            /// </value>
            public bool IsHideButtonChecked
            {
                get
                {
                    return this.isHideButtonChecked;
                }
    
                set
                {
                    this.isHideButtonChecked = value;
                    this.OnPropertyChanged();
                }
            }
    
            /// <summary>
            ///     Gets or sets the selected item.
            /// </summary>
            /// <value>
            ///     The selected item.
            /// </value>
            public object SelectedItem
            {
                get
                {
                    return this.selectedItem;
                }
    
                set
                {
                    this.selectedItem = value;
                    this.OnPropertyChanged();
                }
            }
    
            /// <summary>
            ///     Gets the show hide button command.
            /// </summary>
            /// <value>
            ///     The show hide button command.
            /// </value>
            public ICommand ShowHideButtonCommand
            {
                get
                {
                    return new CommandHandler(
                        () =>
                            {
                                if (this.SelectedItem != null)
                                {
                                    this.IsHideButtonChecked = true;
                                }
                            });
                }
            }
    
            /// <summary>
            /// Gets the unselect all tabs command.
            /// </summary>
            public ICommand UnselectAllTabsCommand
            {
                get
                {
                    return new CommandHandler(() => { this.SelectedItem = null; });
                }
            }
    
            #endregion
    
            #region Methods
    
            /// <summary>
            /// The on property changed.
            /// </summary>
            /// <param name="propertyName">
            /// The property name.
            /// </param>
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChangedEventHandler handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
    
            #endregion
        }
    
        /// <summary>
        /// The command handler.
        /// </summary>
        public class CommandHandler : ICommand
        {
            #region Fields
    
            /// <summary>
            /// The _action.
            /// </summary>
            private readonly Action _action;
    
            /// <summary>
            /// The _can execute.
            /// </summary>
            private readonly bool _canExecute;
    
            #endregion
    
            #region Constructors and Destructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="CommandHandler"/> class.
            /// </summary>
            /// <param name="action">
            /// The action.
            /// </param>
            /// <param name="canExecute">
            /// The can execute.
            /// </param>
            public CommandHandler(Action action, bool canExecute)
            {
                this._action = action;
                this._canExecute = canExecute;
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="CommandHandler"/> class.
            /// </summary>
            /// <param name="action">
            /// The action.
            /// </param>
            public CommandHandler(Action action)
                : this(action, true)
            {
            }
    
            #endregion
    
            #region Public Events
    
            /// <summary>
            /// The can execute changed.
            /// </summary>
            public event EventHandler CanExecuteChanged;
    
            #endregion
    
            #region Public Methods and Operators
    
            /// <summary>
            /// The can execute.
            /// </summary>
            /// <param name="parameter">
            /// The parameter.
            /// </param>
            /// <returns>
            /// The <see cref="bool"/>.
            /// </returns>
            public bool CanExecute(object parameter)
            {
                return this._canExecute;
            }
    
            /// <summary>
            /// The execute.
            /// </summary>
            /// <param name="parameter">
            /// The parameter.
            /// </param>
            public void Execute(object parameter)
            {
                this._action();
            }
    
            #endregion
        }
    }
    

    Remark:

    Note that you need to reference the assembly: System.Windows.Interactivity in your project to invoke command action when your tabControl selectionChanged event is raised.

    The Code above tested and worked correctly for me (see this screen recording in flash video).

    Please try it and let me know.

    I hope that helps you.