How to Stretch WPF Tab Item Headers to Parent Control Width

后端 未结 11 1920
花落未央
花落未央 2020-11-29 23:17

Is there a way in XAML to cause the tab item headers to stretch across the width of the tab control?

For example, I have three tabs: red, blue and green. If I have a

相关标签:
11条回答
  • 2020-11-29 23:31

    I was able to do this using a Converter like so:

    namespace WpfApplication1.Converters
    {
        public class SizeConverter : IValueConverter
        {
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter,
                System.Globalization.CultureInfo culture)
            {
                double width = Double.Parse(value.ToString());
                //Subtract 1, otherwise we could overflow to two rows.
                return .25 * width - 1;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter,
                System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
    
            #endregion
        }
    }
    

    Then adding the namespace to my xaml:

    xmlns:local="clr-namespace:WpfApplication1.Converters"
    

    Then making all of the TabItems use the converter:

    <Window.Resources>
            <local:SizeConverter x:Key="sizeConverter" />
            <Style TargetType="{x:Type TabItem}">
                <Setter Property="Width" Value="{Binding ElementName=x_Grid, Path=ActualWidth, Converter={StaticResource sizeConverter}}" />
            </Style>
        </Window.Resources>
    

    x_Grid is the x:Name of the parent element I want the tabs to be 1/4 of, if that makes sense.

    0 讨论(0)
  • 2020-11-29 23:33

    I took Jordan's example and made some changes to it. This version should work for any number of tabs:

    namespace WpfApplication1.Converters
    {
        public class TabSizeConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter,
                System.Globalization.CultureInfo culture)
            {
                TabControl tabControl = values[0] as TabControl;
                double width = tabControl.ActualWidth / tabControl.Items.Count;
                //Subtract 1, otherwise we could overflow to two rows.
                return (width <= 1) ? 0 : (width - 1);
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
                System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    

    Same namespace in the xaml:

    xmlns:local="clr-namespace:WpfApplication1.Converters"
    

    And this will make all tabs use it:

    <Window.Resources>
        <local:TabSizeConverter x:Key="tabSizeConverter" />
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="Width">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource tabSizeConverter}">
                        <Binding RelativeSource="{RelativeSource Mode=FindAncestor,
                AncestorType={x:Type TabControl}}" />
                        <Binding RelativeSource="{RelativeSource Mode=FindAncestor,
                AncestorType={x:Type TabControl}}" Path="ActualWidth" />
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    
    0 讨论(0)
  • 2020-11-29 23:34

    I don't know if it will work for tabs, but whenever I've needed to stretch anything to fill a container I've used a ViewBox. Is that the kind of thing you're looking for?

    0 讨论(0)
  • 2020-11-29 23:36

    I followed Charlie's suggestion and went on re-templating route. Here is a simple implementation of TabControl that divides available space equally among its TabItems, using UniformGrid:

    Control's XAML

    <TabControl x:Class="YourNamespace.Views.BigTabsTabControl"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:YourNamespace.Views"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300"
                Padding="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
                BorderThickness="1" Foreground="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}">
    
        <TabControl.Resources>
            <SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
            <SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
        </TabControl.Resources>
    
        <TabControl.Style>
            <Style TargetType="{x:Type TabControl}">
                <Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}"/>
                <Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}"/>
            </Style>
        </TabControl.Style>
    
        <TabControl.Template>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition x:Name="ColumnDefinition0"/>
                        <ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
                        <RowDefinition x:Name="RowDefinition1" Height="*"/>
                    </Grid.RowDefinitions>
    
                    <UniformGrid x:Name="headerPanel" Background="Transparent" Grid.Column="0" IsItemsHost="true" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" />
                    <Border x:Name="contentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
                        <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="TabStripPlacement" Value="Bottom">
                        <Setter Property="Grid.Row" TargetName="headerPanel" Value="1"/>
                        <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                        <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                        <Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
                        <Setter Property="Margin" TargetName="headerPanel" Value="2,0,2,2"/>
                    </Trigger>
                    <Trigger Property="TabStripPlacement" Value="Left">
                        <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                        <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                        <Setter Property="Grid.Column" TargetName="headerPanel" Value="0"/>
                        <Setter Property="Grid.Column" TargetName="contentPanel" Value="1"/>
                        <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
                        <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
                        <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                        <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                        <Setter Property="Margin" TargetName="headerPanel" Value="2,2,0,2"/>
                    </Trigger>
                    <Trigger Property="TabStripPlacement" Value="Right">
                        <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                        <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                        <Setter Property="Grid.Column" TargetName="headerPanel" Value="1"/>
                        <Setter Property="Grid.Column" TargetName="contentPanel" Value="0"/>
                        <Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
                        <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
                        <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                        <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                        <Setter Property="Margin" TargetName="headerPanel" Value="0,2,2,2"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </TabControl.Template>
    </TabControl>
    

    Control's Code-Behind

    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    
    namespace YourNamespace.Views
    {
      /// <summary>
      /// A TabControl with large tabs. 
      /// </summary>
      public partial class BigTabsTabControl : TabControl
      {
        public BigTabsTabControl()
        {
          InitializeComponent();
        }
    
        public override void OnApplyTemplate()
        {
          base.OnApplyTemplate();
    
          if (this.Template != null)
          {
            UniformGrid X = this.Template.FindName("headerPanel", this) as UniformGrid;
            if (X != null) X.Columns = this.Items.Count;
          }
        }
      }
    }
    

    That's it. You can now add TabItems to this control and they'll adjust their width automatically. No need to specify Grid.Column for these TabItems either, they work fine without it, even at design time.

    0 讨论(0)
  • 2020-11-29 23:36

    I am using the following solution: In the main window i use a window re sized event and on tabcontrol Initialized event to set the Width of each Tab. The number '5' corresponds to my number of Tabs.

        private void tabchanger_Initialized(object sender, EventArgs e)
        {
            foreach (TabItem item in tabchanger.Items)
            {
                double newW = (tabchanger.ActualWidth / 5) - 1;
                if (newW < 0) newW = 0;
    
                item.Width = newW;
            }
    
        }
    
        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            foreach (TabItem item in tabchanger.Items)
            {
                double newW = (tabchanger.ActualWidth / 5) - 1;
                if (newW < 0) newW = 0;
    
                item.Width = newW;
            }
        }
    
    0 讨论(0)
  • 2020-11-29 23:39

    It is possible by binding the width to the ActualWidth of the parent tab control as shown below.

    I have wrapped it in a style to apply to all tab pages.

    <Grid>
          <Grid.Resources>
            <Style TargetType="TabItem">
                <Setter Property="Width" Value="{Binding    
                         Path=ActualWidth,    
                         RelativeSource={RelativeSource    
                        Mode=FindAncestor,    
                        AncestorType={x:Type TabControl}}}"/>
            </Style>
        </Grid.Resources>
    
    <TabControl>
        <TabItem Header="Page3"/>
        <TabItem Header="Page2"/>
        <TabItem Header="Page3"/>            
    </TabControl> 
    </Grid>
    
    0 讨论(0)
提交回复
热议问题