可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 tab control with its width set to auto, the tab headers will only fill up part of the space above the tab content, but I want them to fill up all the space. For my three tab example, red should take up the first third of the control, blue should take up the center third, and green the final third.
I have an idea how to do this in a code behind which I am working on now, but I am interested in doing this the easiest way possible.
回答1:
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>
回答2:
Everyone seems to be going the converter route, but it really is as simple as using a UniformGrid
with Rows
set to 1 in the TabControl
template, in place of the TabPanel
. Of course, you will have to re-template it, but this isn't too bad.
回答3:
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.
回答4:
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>
回答5:
I am an old school style guy. and prefer this kind of functionality to encapsulate into the code of the control itself. My derived control looks like following:
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;
}
}
}
and my XAML looks like
</infrastructure:CustomTabControl>
<TabItem />
<TabItem />
</infrustracture:CustomControl>
Can someone explain why everyone prefers styling control instead of deriving.
回答6:
I have solved this problem by creating a special converter:
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();
}
}
And I calculate the value of one tab item in the Tag element of the TabControl, to avoid calculating it for each tab separately. Here is the sample code (Note that In my case I needed a horizontal ScrollViewer, because I have multiple tab items, and with a minimum width):
<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>
I can say that this works like a charm in my case :)
Hope someone will find it useful!
P.S. I did not need/want any style in my case.
回答7:
Here is a painless solution that uses Templates only:
<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>
Hope I've included all colors and it will work for you. Ahh... Snap! My Desaturation Effect! My WPF startup project you can grab that effect from there if you want (It's easier to plop the effect in trigger, than to recolor all the thing, same with highlights).
Yes, that's a lot of code, but I simply changed ItemsContainer to look better and replaced standard Header control with UniformGrid and set Rows or Columns to 1 depending on TabStripPlacement. Now I can collapse this code, or hide it somewhere. :)
回答8:
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 TabItem
s, 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 TabItem
s to this control and they'll adjust their width automatically. No need to specify Grid.Column
for these TabItem
s either, they work fine without it, even at design time.
回答9:
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?
回答10:
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;
}
}
回答11:
In addition to Ryan Versaw's accepted solution which gives equal tabItem header widths, I found the following way to make it dependent on the length of each header.
First we get the string of each tabItem header by adding this line to the xaml multibinding. Thus it becomes:
<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>
And a bit more of code in the Converter (values[] gets also the tabItem Header):
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;
}
I suspect there might be a more efficient way to get the AllHeaders string of all the headers, but it works fine as it is ...