Closeable TabItems using CompositeWPF
On a project I am working on at the moment, I needed to allow the user to open and close views. As I was using the Composite Application Block injecting views into a tab control was really easy. Closing them was not.
To remove a view from a region you need two things, the RegionName and the View.
This article helped me a lot when engineering this approach:
http://blogs.infosupport.com/blogs/willemm/archive/2008/07/31/Creating-closeable-tabitems-for-use-in-CompositeWPF.aspx
However the RegionManager.GetRegionName did not work against the TabItem. I always got null, I am assuming this is a change that was made during the time the article was posted. Using the TabControl however worked.
But you cannot easily bind to two things (the TabControl and the TabItem).
To achieve my goal I used the XAML from the article and changed it little
<DataTemplate x:Key="CustomTabHeader">
<StackPanel Orientation="Horizontal">
<ContentPresenter>
<ContentPresenter.Content>
<Binding Path="Content.Model.HeaderText">
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor"
AncestorType="{x:Type TabItem}"/>
</Binding.RelativeSource>
</Binding>
</ContentPresenter.Content>
</ContentPresenter>
<Button Margin="8,0,0,0"
Command="{Binding Path=Model.CloseTabCommand.Command, ElementName=ThisControl}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TabItem}}}"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Grid>
<Canvas Width="8" Height="8">
<Line X1="2" X2="6" Y1="2" Y2="6" Stroke="Black" StrokeThickness="1"/>
<Line X1="6" X2="2" Y1="2" Y2="6" Stroke="Black" StrokeThickness="1"/>
</Canvas>
</Grid>
</Button>
</StackPanel>
</DataTemplate>
<Style TargetType="TabItem">
<Style.Setters>
<Setter Property="HeaderTemplate"
Value="{StaticResource CustomTabHeader}"/>
</Style.Setters>
</Style>
The only difference is where I go for my commands and header text. In my project I have an interface that describes a headed content item.
The change really is in the code for closing the view:
class CloseTabCommand : CommandModelBase
{
private readonly IRegionManager regionManager;
public CloseTabCommand(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
public override void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
var tabItem = (TabItem)e.Parameter;
var tabControl = (TabControl) e.Source;
var regionName = RegionManager.GetRegionName(tabControl);
regionManager.Regions[regionName].Remove(tabItem.Content);
}
}
In the end the solution boiled down to being really simple. You still bind to the tab item but the source is the tab control.
There will be some drawbacks to this approach. If a key binding is setup for this command then the source could change, so care should be taken.
But this works and it will do for the time being until I think of a better solution.