Modifying TabControl's header

binding, datatemplate, mvvm, wpf, xaml

Solution

OK. I solved it myself. For anyone else trying to implement a VS-like Close button and unsaved changes asterisk, here's the template:

<TabControl.ItemContainerStyle>
    <Style TargetType="{x:Type TabItem}">
        <Setter Property="HeaderTemplate" >
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0" Height="22">
                        <TextBlock VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource AncestorType=TabItem}, Path=Content.HeaderText}" />
                        <TextBlock Text=" *" ToolTip="Has unsaved changes" Visibility="{Binding Content.DataContext.HasChanges, RelativeSource={RelativeSource AncestorType=TabItem}, Converter={StaticResource B2VConverter}}" />
                        <Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Width="18" Height="18" 
                                Margin="6,0,0,0" Padding="0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center" 
                                Command="{Binding DataContext.TabClosingCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"
                                VerticalAlignment="Center" Focusable="False">
                            <Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                <Line StrokeThickness="3" StrokeStartLineCap="Round"  StrokeEndLineCap="Round" Stroke="Gray" X1="1" Y1="1" X2="9" Y2="9" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                <Line StrokeThickness="3" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="Gray" X1="1" Y1="9" X2="9" Y2="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
                            </Grid>
                        </Button>
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</TabControl.ItemContainerStyle>

Results in an elegant drawing-based button with a flat-look. Your View must implement Boolean `HasChanges` and `HeaderText` properties, plus you need to define a `BooleanToVisibilityConverter` in your resources section, named B2VConverter.

Problem

So I'm adding my views directly to the `TabControl`'s Items collection at runtime (instead of creating TabItems around them and addings those TabItems to TabControl). The views expose a property (wrapper around a ViewModel property of the same name) named `HasChanges` that I want to bind to `TabItem`'s Header to show a Asterisk (*) sign to identify tabs with unsaved changes, just like VS does. I have already tried using DataTemplates but am having trouble accessing the view object in the `DataTemplate`. What's the correct way of doing this? Here's one of my several attempts: ``` <TabControl.ItemTemplate> <DataTemplate DataType="UserControl"> <StackPanel Orientation="Horizontal" Margin="0" Height="22"> <TextBlock VerticalAlignment="Center" Text="{Binding HeaderText, RelativeSource={RelativeSource AncestorType=UserControl}}" /> <TextBlock Text="*" Visibility="{Binding HasChanges, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource B2VConverter}}" /> </StackPanel> </DataTemplate> </TabControl.ItemTemplate> ``` Note that I'm trying two different binding methods for the two `TextBlock`s, none of which is working. My views inherit from `UserControl` and expose properties `HasChanges` and `HeaderText`.

Original source