Cómo estirar los encabezados de los elementos de la pestaña WPF al ancho del control principal
xaml tabs (11)
Además de la solución accedida de Ryan Versaw que ofrece anchos de encabezado de artículos de tabla iguales, encontré la siguiente manera de hacerlo dependiente de la longitud de cada encabezado.
Primero obtenemos la cadena de cada encabezado tabItem agregando esta línea al multibinding xaml. Así se convierte en:
<MultiBinding Converter="{StaticResource tabSizeConverter}">
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}" />
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}" Path="ActualWidth" />
<Binding Path="Header" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
Y un poco más de código en el convertidor (los valores [] también reciben el encabezado del elemento de tabla):
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
TabControl tabControl = values[0] as TabControl;
string AllHeaders = "";
for (int i = 0; i < tabControl.Items.Count; i++)
{
int index = tabControl.Items[i].ToString().IndexOf("Header:") + "Header:".Length;
string currentHeader = tabControl.Items[i].ToString().Substring(index);
currentHeader = currentHeader.Substring(0, currentHeader.Length - " Content:".Length);
AllHeaders += currentHeader;
}
//Normalize width according to header length
double width = values[2].ToString().Length * tabControl.ActualWidth / AllHeaders.Length;
//Subtract 1, otherwise we could overflow to two rows.
var retVal = (width <= 1) ? 0 : (width - 1);
return retVal;
}
Sospecho que podría haber una manera más eficiente de obtener la cadena AllHeaders de todos los encabezados, pero funciona bien como está ...
¿Hay alguna forma en XAML de hacer que los encabezados de los elementos de la pestaña se extiendan a lo ancho del control de la pestaña?
Por ejemplo, tengo tres pestañas: rojo, azul y verde. Si tengo un control de pestaña con su ancho configurado en automático, los encabezados de la pestaña solo llenarán parte del espacio sobre el contenido de la pestaña, pero quiero que llenen todo el espacio. Para mi ejemplo de tres pestañas, el rojo debería ocupar el primer tercio del control, el azul debería ocupar el tercio central y el verde el tercio final.
Tengo una idea de cómo hacer esto en un código detrás del cual estoy trabajando ahora, pero estoy interesado en hacerlo de la manera más fácil posible.
Aquí hay una solución indolora que usa plantillas solamente:
<Window x:Class="Window1"
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"
xmlns:EffectLibrary="clr-namespace:EffectLibrary;assembly=EffectLibrary"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<TabControl Style="{DynamicResource TabControlStyle}" ItemContainerStyle="{DynamicResource TabItemStyle}" BorderBrush="{DynamicResource Pallete.Primary}" Foreground="{DynamicResource Pallete.Primary}" Background="Transparent" Margin="0" d:LayoutOverrides="Height">
<TabControl.Resources>
<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="#093A5F"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="#001423"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Border x:Name="Bg" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<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" IsItemsHost="True" Margin="0">
<UniformGrid.Style>
<Style TargetType="{x:Type UniformGrid}">
<Setter Property="Rows" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource TemplatedParent}}" Value="Right">
<Setter Property="Columns" Value="1"/>
<Setter Property="Rows" Value="0"/>
</DataTrigger>
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource TemplatedParent}}" Value="Left">
<Setter Property="Columns" Value="1"/>
<Setter Property="Rows" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UniformGrid.Style>
</UniformGrid>
<Border x:Name="contentPanel" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local" BorderThickness="0,1,0,0" BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</Border>
<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"/>
</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"/>
</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"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Effect" TargetName="templateRoot">
<Setter.Value>
<EffectLibrary:DesaturateEffect DesaturationFactor=".25"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}"/>
<Setter Property="Background" Value="{Binding Background, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}"/>
<Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0,5"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true" Background="{TemplateBinding Background}">
<Border x:Name="mainBorder" BorderBrush="{TemplateBinding BorderBrush}">
<Border x:Name="highlightBorder"/>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Effect" TargetName="templateRoot">
<Setter.Value>
<EffectLibrary:DesaturateEffect DesaturationFactor=".25"/>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true">
<Setter TargetName="highlightBorder" Property="Background" Value="#0B79CE"/>
</DataTrigger>
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top">
<Setter TargetName="mainBorder" Property="BorderThickness" Value="0,0,1,0"/>
<Setter TargetName="highlightBorder" Property="Height" Value="2"/>
<Setter TargetName="highlightBorder" Property="VerticalAlignment" Value="Bottom"/>
</DataTrigger>
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom">
<Setter TargetName="mainBorder" Property="BorderThickness" Value="0,0,1,0"/>
<Setter TargetName="highlightBorder" Property="Height" Value="2"/>
<Setter TargetName="highlightBorder" Property="VerticalAlignment" Value="Top"/>
</DataTrigger>
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left">
<Setter TargetName="mainBorder" Property="BorderThickness" Value="0,0,0,1"/>
<Setter TargetName="highlightBorder" Property="Width" Value="2"/>
<Setter TargetName="highlightBorder" Property="HorizontalAlignment" Value="Right"/>
</DataTrigger>
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right">
<Setter TargetName="mainBorder" Property="BorderThickness" Value="0,0,0,1"/>
<Setter TargetName="highlightBorder" Property="Width" Value="2"/>
<Setter TargetName="highlightBorder" Property="HorizontalAlignment" Value="Left"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Years">
<ListBox Background="{DynamicResource Pallete.Primary.Brightest}" Foreground="{DynamicResource Pallete.Primary}">
<TextBlock Text="2015"/>
<TextBlock Text="2016"/>
<TextBlock Text="2017"/>
</ListBox>
</TabItem>
<TabItem Header="Tables">
<ListBox Background="{DynamicResource Pallete.Primary.Brightest}" Foreground="{DynamicResource Pallete.Primary}">
<TextBlock Text="Table1..."/>
<TextBlock Text="Table2..."/>
<TextBlock Text="Table3..."/>
</ListBox>
</TabItem>
</TabControl>
</Window>
Espero haber incluido todos los colores y funcionará para ti. Ahh ... Snap! ¡Mi efecto de desaturación! Mi proyecto de inicio de WPF puede capturar ese efecto desde allí si lo desea (es más fácil presionar el efecto en el desencadenador que recolorearlo todo, lo mismo que con los resaltados). Sí, eso es un montón de código, pero simplemente cambié ItemsContainer para lucir mejor y reemplacé el control de encabezado estándar con UniformGrid y configuré Filas o Columnas a 1 dependiendo de TabStripPlacement. Ahora puedo colapsar este código, u ocultarlo en algún lugar. :)
Es posible vincular el ancho al Ancho Real del control de la pestaña principal como se muestra a continuación.
Lo he envuelto en un estilo para aplicarlo a todas las páginas de pestañas.
<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>
Estoy usando la siguiente solución: En la ventana principal, uso un evento de tamaño de la ventana y en el evento Inicializado de control de tabulación para configurar el Ancho de cada pestaña. El número ''5'' corresponde a mi número de pestañas.
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;
}
}
He resuelto este problema creando un convertidor especial:
public class TabItemWidthAdjustmentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Double lTabControlWidth = value is Double ? (Double)value : 50; // 50 just to see something, in case of error
Int32 lTabsCount = (parameter != null && parameter is String) ? Int32.Parse((String)parameter) : 1;
return lTabControlWidth / lTabsCount;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Y calculo el valor de un elemento de pestaña en el elemento Etiqueta del control de tabla, para evitar calcularlo para cada pestaña por separado. Aquí está el código de ejemplo (Tenga en cuenta que en mi caso necesitaba un ScrollViewer horizontal, porque tengo varios elementos de pestañas y con un ancho mínimo):
<TabControl Name="tabControl" VerticalAlignment="Stretch" SelectionChanged="TabControl_SelectionChanged"
Tag="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth, Converter={StaticResource tabItemWidthAdjustmentConverter}, ConverterParameter=15}"><!-- Here 15 because I have 15 tabs -->
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex="1"
KeyboardNavigation.TabIndex="1"
IsItemsHost="True"/>
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"/>
</StackPanel>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="Tab1" MinWidth="115" VerticalAlignment="Stretch" Width="{Binding ElementName=tabControl, Path=Tag}">
<ContentControl ContentTemplate="{StaticResource My_TemplateTab1}">
<ContentPresenter />
</ContentControl>
</TabItem>
<TabItem Header="Tab2" MinWidth="115" Height="50" Width="{Binding ElementName=tabControl, Path=Tag}">
<ContentControl ContentTemplate="{StaticResource My_TemplateTab2}">
<ContentPresenter />
</ContentControl>
</TabItem>
<!-- Here another 13 tabs which I skipped -->
</TabControl>
Puedo decir que esto funciona como un encanto en mi caso :) ¡Espero que alguien lo encuentre útil!
PD No necesitaba / quería ningún estilo en mi caso.
No sé si funcionará para las pestañas, pero siempre que necesito estirar algo para llenar un contenedor, he usado un ViewBox . ¿Es ese el tipo de cosa que estás buscando?
Pude hacer esto usando un convertidor así:
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
}
}
Luego agregando el espacio de nombres a mi xaml:
xmlns:local="clr-namespace:WpfApplication1.Converters"
Luego, haciendo que todos los TabItems usen el convertidor:
<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 es el x: Nombre del elemento padre Quiero que las pestañas sean 1/4 de, si eso tiene sentido.
Seguí la sugerencia de Charlie y seguí replanteando la ruta. Aquí hay una implementación simple de TabControl
que divide el espacio disponible de manera equitativa entre sus TabItem
s, usando UniformGrid
:
Control de 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>
Código de control detrás de
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;
}
}
}
}
Eso es. Ahora puede agregar TabItem
s a este control y ellos ajustarán su ancho automáticamente. No es necesario especificar Grid.Column
para estas TabItem
s, funcionan bien sin ella, incluso en el momento del diseño.
Soy un chico de estilo de la vieja escuela. y prefiere este tipo de funcionalidad para encapsular en el código del propio control. Mi control derivado se parece a lo siguiente:
public class CustomTabControl :TabControl
{
protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
{
foreach (TabItem item in this.Items)
{
double newW = (this.ActualWidth / Items.Count) - 1;
if (newW < 0) newW = 0;
item.Width = newW;
}
}
}
y mi XAML parece
</infrastructure:CustomTabControl>
<TabItem />
<TabItem />
</infrustracture:CustomControl>
Alguien puede explicar por qué todo el mundo prefiere el control de estilo en lugar de derivar.
Todo el mundo parece ir por la ruta del convertidor, pero en realidad es tan simple como usar un UniformGrid
con Rows
establecidas en 1 en la plantilla TabControl
, en lugar de TabPanel
. Por supuesto, tendrá que volver a colocarlo en la plantilla, pero esto no es tan malo.
Tomé el ejemplo de Jordan y le hice algunos cambios. Esta versión debería funcionar para cualquier número de pestañas:
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();
}
}
}
Mismo espacio de nombres en el xaml:
xmlns:local="clr-namespace:WpfApplication1.Converters"
Y esto hará que todas las pestañas lo utilicen:
<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>