views:

4775

answers:

4

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. I have a tab control with it's width set to auto, the tab headers will only fill up part of the space above the tab content, I want them to fill up all the space.

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.

Edit--4/7/08

More specifically if I have 4 tabs I want each tab to take up 25% of the space.

A: 

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?

Matthew Steeples
A: 

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>
John
This would just make every TabItem occupy the full width. What is needed, they need to share the width.
Sergey Aldoukhov
Agreed, this was written before the question was edited.
John
+2  A: 

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.

Jordan H.
I apologize for the tremendous amount of whitespace below the code. I don't know why it's doing that.
Jordan H.
Also, you can make the converter as complicated as you want. You could go get the TabControl, look at its Items and see how many there are, and then use that to calculate your width.
Jordan H.
Jordan: Thanks mate, this works a treat :) P.S. "Object Value" comes into the Convert function as a double, so you can just cast it "double height = (double)value;" +1 BTW :)
Binary Worrier
+5  A: 

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>
Ryan Versaw
I would cap the return value of the converter with return width < 0 ? 0 : width - 1otherwise it could raise an exception
Sergey Aldoukhov
Will the width ever actually be less than 0 though? It might be able to, but either way I'll include this but with a minor modification. It should actually be `return (width <= 1) ? 0 : (width - 1);` in case width is between 0 and 1 to prevent a negative result.
Ryan Versaw