tags:

views:

282

answers:

3

I am creating a UI in which a TabControl has a series of standard TabItems with ordinary WPF controls as their content. The headers just display ordinary strings as usual. However, I want the header of the last item to contain a Button instead of an ordinary string.

Let me stress that I already know how to do this without bindings. I want to do it with bindings. Specifically, the TabControl is bound to an array of UserControls, except for the last object which is a Button. It is this Button that I want to appear in the header instead of as the content of the last TabItem. (I frankly don't care what the content of the last TabItem is. Only the header.)

I understand DataTemplateSelectors and StyleSelectors. What I can't figure out is what combination of styles and templates will do this for me. Specifically, I need to know what the Style and DataTemplate for the last item should look like, so that I can select them appropriately. No other code need be shown.

A: 

I figured it out myself. I'm new to WPF/XAML, and the insight I just had is that, in the case of an ItemsControl like TabControl, a DataTemplate lays out the content of each item, but says nothing about the overall layout of the item itself, which is managed by its ControlTemplate. Now I realize I don't need an DataTemplateSelector at all, just a style for the last item, like so:


            <Style x:Key="buttonStyle" TargetType="TabItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TabItem">
                            <Border>
                                <ContentPresenter
                                    Margin="4,1,0,1"
                                    Content="{Binding}"
                                    ContentSource="Header">
                                </ContentPresenter>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>


Gregory Higley
If I had seen this I wouldn't have bothered with most of my reply, but you posted this while I was writing it. However I would remove the ContentSource attribute from the template as it is not needed. Alternatively you could change it to Content and take out the Content attribute.
Caleb Vear
If I take out the ContentSource attribute the button will not appear in the header.
Gregory Higley
A: 

The first thing I would try is to set the style of the last TabItem. In the style if you bind the Header property to the Content property that should make the button appear in the TabItems header. Then you will would want a DataTemplate that is invisible for the item so that you don't see it in the tab's content area.

If that diesn't work then you may need to make a new ControlTemplate for the tab item. The default template looks like this:

<Grid SnapsToDevicePixels="true">
<Border Name="Bd" Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="1,1,1,0">
    <ContentPresenter Name="Content" 
         ContentSource="Header" 
         SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
         RecognizesAccessKey="true" 
         HorizontalAlignment="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" 
                VerticalAlignment="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
</Border>

The only thing you would have to change is the ContentSource from Header to Content. You should create a copy of the original to do this in though, because otherwise you will miss out on all the relevant triggers. You can find the original using the BAML viewer extension for Reflector. Or you could get the original in Blend if you have that.

Caleb Vear
A: 

There is yet another way to do this that's even nicer.

<TabControl>
    <TabControl.ItemsSource>
     <CompositeCollection>
  <CollectionContainer Collection="{StaticResource items}" />
      <TabItem>
   <TabItem.Template>
        <ControlTemplate>
         <Border Name="PART_Header" Margin="4,0,1,1">            
          <Button Click="AddTicket">Add Ticket</Button>
     </Border>
    </ControlTemplate>
       </TabItem.Template>
      </TabItem>
     </CompositeCollection>
    </TabControl.ItemsSource>
</TabControl>

(Please note that the above was done entirely from memory. I use a Mac and didn't want to boot into my VM just for this. It should be fairly accurate.)

Gregory Higley