I am creating a UI in which a TabControl
has a series of standard TabItem
s 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 UserControl
s, 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 DataTemplateSelector
s and StyleSelector
s. 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.
-
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>
Caleb Vear : 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.Gregory Higley : If I take out the ContentSource attribute the button will not appear in the header. -
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.
-
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.)
0 comments:
Post a Comment