Search code examples
wpfmvvmtabitem

Binding to Button Visibility


I have the following Style for a TabItem

<Style x:Key="SubStudioTabItem" TargetType="{x:Type TabItem}">
  <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
  <Setter Property="Background" Value="Transparent"/>
  <Setter Property="Template">
     <Setter.Value>
        <ControlTemplate TargetType="{x:Type TabItem}">
           <Grid Height="20" 
                        Background="{TemplateBinding Background}" 
                        SnapsToDevicePixels="True">
              <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="Auto"/>
                 <ColumnDefinition Width="20"/>
              </Grid.ColumnDefinitions>
              <ContentPresenter Grid.Column="0" 
                                Margin="10,0,10,0" 
                                HorizontalAlignment="Center" 
                                VerticalAlignment="Center" 
                                ContentSource="Header" />
              <Button Grid.Column="1" 
                      x:Name="CloseButton" 
                      Width="15" 
                      Height="15" 
                      HorizontalAlignment="Center" 
                      VerticalAlignment="Center" 
                      DockPanel.Dock="Right" 
                      AttachedCommand:CommandBehavior.Event="Click"
                      AttachedCommand:CommandBehavior.Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, 
                                                                        Path=DataContext.CloseWorkspaceCommand}">
              ...

Now I want to make the visibility of the Button on the TabItem optional which can be used in a DataTemplate like

<DataTemplate x:Key="WorkspaceTemplate">
    <TabControl x:Name="tabControl" 
                IsSynchronizedWithCurrentItem="true" 
                Style="{StaticResource StudioTabControl}"
                ItemsSource="{Binding Workspaces}" 
                SelectedIndex="{Binding SelectedIndex, NotifyOnSourceUpdated=True, Mode=TwoWay}">
        <TabControl.ItemContainerStyle>
            <Style TargetType="TabItem" 
                   BasedOn="{StaticResource SubStudioTabItem}">
                <Setter Property="??Button.Visibility??" Value="{Binding Path=Display, Converter={StaticResource BooleanToVisibiltyConverter}}"/>
            </Style>
        </TabControl.ItemContainerStyle>
    </TabControl>
</DataTemplate>

How can I set the state of the visibility of the button on the TabItem from the DataTemplate?

Thanks for your time.


Solution

  • In that case, I would create an attached property, such as type of Visibility and would make a TemplateBinding in the Style, something like this:

    <ControlTemplate TargetType="{x:Type TabItem}">
    ...
        <Button Grid.Column="1" 
                    x:Name="CloseButton" 
                    Visibility="{TemplateBinding local:MyClass.ButtonVisibility}"
                    ...
        </Button>
    

    And for TabItem in <ItemContainerStyle> would write this (or somewhere else):

    <Style TargetType="TabItem" BasedOn="{StaticResource SubStudioTabItem}">
        <Setter Property="local:MyClass.ButtonVisibility" Value="{Binding Path=Display, Converter={StaticResource BooleanToVisibiltyConverter}}"/>
    </Style>
    

    Edit:

    I created a project that implements this method. In the project tried to follow the MVVM pattern. The structure of the project:

    enter image description here

    Start in order, in folder AttachedProperties there is an attached property, which is responsible for the appearance of the Button.

    The code of ButtonVisibility.cs:

    using System;
    using System.Windows;
    
    public class ButtonVisibilityPro : DependencyObject
    {
        public static readonly DependencyProperty ButtonVisibilityProperty;
    
        public static void SetButtonVisibility(DependencyObject DepObject, Visibility value)
        {
            DepObject.SetValue(ButtonVisibilityProperty, value);
        }
    
        public static Visibility GetButtonVisibility(DependencyObject DepObject)
        {
            return (Visibility)DepObject.GetValue(ButtonVisibilityProperty);
        }
    
        static ButtonVisibilityPro()
        {
            PropertyMetadata MyPropertyMetadata = new PropertyMetadata(Visibility.Collapsed);
    
            ButtonVisibilityProperty = DependencyProperty.RegisterAttached("ButtonVisibility",
                                                                typeof(Visibility),
                                                                typeof(ButtonVisibilityPro),
                                                                MyPropertyMetadata);
        }
    }
    

    The data model is ButtonModel, wherein the boolean property is ButtonDisplay. This class inherits from the ViewModelBase, that implements INotifyPropertyChanged.

    ButtonModel.cs

    using System;
    using ButtonVisibilityHelp.ViewModels;
    
    namespace ButtonVisibilityHelp.Models
    {
        public class ButtonModel : ViewModelBase
        {
            private bool _buttonDisplay = false;
    
            public bool ButtonDisplay
            {
                get 
                {
                    return _buttonDisplay; 
                }
    
                set
                {
                    _buttonDisplay = value;
                    NotifyPropertyChanged("ButtonDisplay");             
                }
            }
        }
    }
    

    ViewModelBase.cs

    using System;
    using System.ComponentModel;
    
    namespace ButtonVisibilityHelp.ViewModels
    {
        public class ViewModelBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    

    ButtonViewModel.cs

    using System;
    using System.Windows.Input;
    using System.Windows;
    
    using ButtonVisibilityHelp.Models;
    using ButtonVisibilityHelp.Workers;
    
    namespace ButtonVisibilityHelp.ViewModels
    {
        public class ButtonViewModel : ViewModelBase
        {
            private ButtonModel _buttonModel;
    
            private ICommand _hideButtonCommand = null;
            private ICommand _showButtonCommand = null;
    
            public ButtonModel ButtonModel
            {
                get
                {
                    return _buttonModel;
                }
    
                set
                {
                    _buttonModel = value;
                    NotifyPropertyChanged("ButtonModel");
                }
            }
    
            public ICommand HideButtonCommand
            {
                get
                {
                    if (_hideButtonCommand == null)
                    {
                        _hideButtonCommand = new RelayCommand(param => this.HideButton(), null);
                    }
    
                    return _hideButtonCommand;
                }
            }
    
            public ICommand ShowButtonCommand
            {
                get
                {
                    if (_showButtonCommand == null)
                    {
                        _showButtonCommand = new RelayCommand(param => this.ShowButton(), null);
                    }
    
                    return _showButtonCommand;
                }
            }
    
            public ButtonViewModel()
            {
                ButtonModel = new ButtonModel();
            }
    
            private void HideButton() 
            {            
                ButtonModel.ButtonDisplay = false;
            }
    
            private void ShowButton() 
            {
                ButtonModel.ButtonDisplay = true;
            }
        }
    }   
    

    RelayCommand.cs

    using System;
    using System.Windows.Input;
    
    namespace ButtonVisibilityHelp.Workers
    {
        public class RelayCommand : ICommand
        {
            private readonly Action<object> _execute;
            private readonly Predicate<object> _canExecute;
    
            public RelayCommand(Action<object> execute) : this(execute, null)
            {
            }
    
            public RelayCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                {
                    throw new ArgumentNullException("execute");
                }
    
                _execute = execute;
                _canExecute = canExecute;
            }
    
            public bool CanExecute(object parameter)
            {
                return _canExecute == null ? true : _canExecute(parameter);
            }
    
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                }
    
                remove
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
    
            public void Execute(object parameter)
            {
                _execute(parameter);
            }
        }
    }
    

    Below is a piece of XAML code that is in the style of TabItem:

    <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabItem}">
                    <Grid Background="{TemplateBinding Background}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="20"/>
                        </Grid.ColumnDefinitions>
    
                        <Border Grid.Column="0" SnapsToDevicePixels="True" Name="Border" Margin="0,0,2,0" Padding="2" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0">
                            <ContentPresenter Name="ContentSite" 
                                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                  Margin="5,5,5,5"
                                                  VerticalAlignment="Center" 
                                                  RecognizesAccessKey="True" 
                                                  ContentSource="Header" />                            
                        </Border>
    
                        <!-- Here TemplateBinding for Visibility -->
                        <Button Name="CloseButton" Style="{StaticResource CloseButton}" 
                                    Visibility="{TemplateBinding AttachedProperties:ButtonVisibilityPro.ButtonVisibility}" 
                                    Grid.Column="1" Width="14" Height="14" HorizontalAlignment="Center" />
                    </Grid>
    
                    <ControlTemplate.Triggers>
                        <Trigger Property="TabStripPlacement" Value="Bottom">
                            <Setter TargetName="Border" Property="CornerRadius" Value="0,0,0,0" />
                        </Trigger>                            
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
    </Setter>   
    

    XAML of MainWindow:

    <Window x:Class="ButtonVisibilityHelp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ViewModels="clr-namespace:ButtonVisibilityHelp.ViewModels"
        xmlns:AttachedProperties="clr-namespace:ButtonVisibilityHelp.AttachedProperties"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="350" Width="525">
    
        <Window.Resources>
            <ViewModels:ButtonViewModel x:Key="MyButtonViewModel" />
            <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        </Window.Resources>
    
        <Grid DataContext="{Binding Source={StaticResource MyButtonViewModel}}">
            <TabControl>
                <TabControl.ItemContainerStyle>
                    <Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}">
                        <Setter Property="AttachedProperties:ButtonVisibilityPro.ButtonVisibility" Value="{Binding Path=ButtonModel.ButtonDisplay, Converter={StaticResource BooleanToVisibilityConverter}}" />
                    </Style>
                </TabControl.ItemContainerStyle>
    
                <TabItem Header="Test1">
                    <StackPanel>
                        <TextBlock Text="{Binding ButtonModel.ButtonDisplay, StringFormat=ButtonDisplay: {0}, Mode=TwoWay}" HorizontalAlignment="Right" />
    
                        <Button Name="ShowInTest1" Command="{Binding ShowButtonCommand}" Width="100" Height="30" Content="Show me" HorizontalAlignment="Right" />
                        <Button Name="HideInTest1" Command="{Binding HideButtonCommand}" Width="100" Height="30" Content="Hide me" HorizontalAlignment="Right" />                   
                    </StackPanel>
                </TabItem>
    
                <TabItem Header="Test2">
                    <StackPanel>
                        <TextBlock Text="{Binding ButtonModel.ButtonDisplay, StringFormat=ButtonDisplay: {0}, Mode=TwoWay}" HorizontalAlignment="Right" />
    
                        <Button Name="ShowInTest2" Command="{Binding ShowButtonCommand}" Width="100" Height="30" Content="Show me" HorizontalAlignment="Right" />
                        <Button Name="HideInTest2" Command="{Binding HideButtonCommand}" Width="100" Height="30" Content="Hide me" HorizontalAlignment="Right" />
                    </StackPanel>
                </TabItem>
            </TabControl>
        </Grid>
    </Window>
    

    Output

    Show me:

    enter image description here

    Hide me:

    enter image description here

    The complete code example is available at this link.