How to Add Close Button to the Tab Headers

Since R2 2019 the RadTabItem supports built-in close and pin buttons. Read more about this in the Pin and Close article.

The goal of this tutorial is to create a tab control with closable tab items. The tab items can be closed using close button placed in the tab header as shown on the snapshot bellow. Silverlight RadTabControl Add Close Button to the Tab Headers

For the purpose of this example, you will need to create an empty Silverlight Application project and open it in Visual Studio.

If you copy and paste the source code directly from this XAML examples, don't forget to change xmlns:example alias to import the namespace used in your project.

  • The first step is to add references to the assemblies Telerik.Windows.Controls and Telerik.Windows.Controls.Navigation.

  • In order to create closable tab control, we are going to make use of the routed events mechanism. That is why you will create a helper class named RoutedEventHelper. It will contain the declarations of both the close tab routed event and the attached property for enabling the routed event for the close button. Here is how this class looks like:

public class RoutedEventHelper 
{ 
    //Create the routed event: 
    public static readonly RoutedEvent CloseTabEvent = EventManager.RegisterRoutedEvent( 
        "CloseTab", 
        RoutingStrategy.Bubble, 
        typeof(RoutedEventHandler), 
        typeof(RoutedEventHelper)); 
    //Add an attached property: 
    public static bool GetEnableRoutedClick(DependencyObject obj) 
    { 
        return (bool)obj.GetValue(EnableRoutedClickProperty); 
    } 
    public static void SetEnableRoutedClick(DependencyObject obj, bool value) 
    { 
        obj.SetValue(EnableRoutedClickProperty, value); 
    } 
    // Using a DependencyProperty as the backing store for EnableRoutedClick. 
    // This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty EnableRoutedClickProperty = DependencyProperty.RegisterAttached( 
        "EnableRoutedClick", 
        typeof(bool), 
        typeof(RoutedEventHelper), 
        new System.Windows.PropertyMetadata(OnEnableRoutedClickChanged)); 
    private static void OnEnableRoutedClickChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
        var newValue = (bool)e.NewValue; 
        var button = sender as Button; 
        if (button == null) 
            return; 
        if (newValue) 
            button.Click += new RoutedEventHandler(OnButtonClick); 
    } 
    static void OnButtonClick(object sender, RoutedEventArgs e) 
    { 
        var control = sender as Control; 
        if (control != null) 
        { 
            control.RaiseEvent(new RadRoutedEventArgs(RoutedEventHelper.CloseTabEvent, control)); 
        } 
    } 
} 
Public Class RoutedEventHelper 
    'Create the routed event:' 
        Public Shared ReadOnly CloseTabEvent As RoutedEvent = EventManager.RegisterRoutedEvent("CloseTab", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(RoutedEventHelper)) 
    'Add an atached property:' 
    Public Shared Function GetEnableRoutedClick(ByVal obj As DependencyObject) As Boolean 
        Return CBool(obj.GetValue(EnableRoutedClickProperty)) 
    End Function 
    Public Shared Sub SetEnableRoutedClick(ByVal obj As DependencyObject, ByVal value As Boolean) 
        obj.SetValue(EnableRoutedClickProperty, value) 
    End Sub 
    ' Using a DependencyProperty as the backing store for EnableRoutedClick. This enables animation, styling, binding, etc...' 
    Public Shared ReadOnly EnableRoutedClickProperty As DependencyProperty = DependencyProperty.RegisterAttached("EnableRoutedClick", GetType(Boolean), GetType(RoutedEventHelper), New System.Windows.PropertyMetadata(New PropertyChangedCallback(AddressOf OnEnableRoutedClickChanged))) 
    Private Shared Sub OnEnableRoutedClickChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs) 
        Dim newValue = CBool(e.NewValue) 
        Dim button = TryCast(sender, Button) 
        If button Is Nothing Then 
            Exit Sub 
        End If 
        If newValue Then 
            AddHandler button.Click, AddressOf OnButtonClick 
        End If 
    End Sub 
    Private Shared Sub OnButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs) 
        Dim control = TryCast(sender, Control) 
        If control IsNot Nothing Then 
            control.RaiseEvent 
        End If 
    End Sub 
End Class 

As you can see, you have declared a new routed event called CloseTabEvent of type Telerik.Windows.RoutedEvent. For its creation you have used the static method DeclareRoutedEvent from the class Telerik.Windows.EventManager. This routed event will be raised when the close button of the tab control is clicked by the user.

Another thing that is worth mentioning is the declaration of the attached property EnableRoutedClickProperty. Using this property you can enable the raise of the close routed event for certain button control. See how this property is set from XAML in the next step.

  • Change the XAML of MainPage.xaml to:

<UserControl.Resources> 
    <Style x:Key="ClosableStyle" TargetType="telerik:RadTabItem"> 
        <Setter Property="HeaderTemplate"> 
            <Setter.Value> 
              <DataTemplate> 
                        <Grid> 
                            <Grid.ColumnDefinitions> 
                                <ColumnDefinition Width="*" /> 
                                <ColumnDefinition Width="Auto" /> 
                            </Grid.ColumnDefinitions> 
                            <ContentControl Content="{Binding Title}" /> 
                            <telerik:RadButton Grid.Column="1"  
                                               Width="16" 
                                               Height="16" 
                                               Margin="3 0 0 0" 
                                               HorizontalAlignment="Center" 
                                               VerticalAlignment="Center" 
                                               Content="x" 
                                               example:RoutedEventHelper.EnableRoutedClick="True" 
                                               Padding="0" /> 
                        </Grid> 
               </DataTemplate> 
            </Setter.Value> 
        </Setter> 
        <Setter Property="ContentTemplate"> 
            <Setter.Value> 
                <DataTemplate> 
                    <Grid> 
                        <TextBlock Text="{Binding Content}" TextAlignment="Center" VerticalAlignment="Center"/> 
                    </Grid> 
                </DataTemplate> 
            </Setter.Value> 
        </Setter> 
    </Style> 
</UserControl.Resources> 
<Grid x:Name="LayoutRoot" Background="White"> 
    <telerik:RadTabControl x:Name="tabControl" ItemContainerStyle="{StaticResource ClosableStyle}"> 
    </telerik:RadTabControl> 
</Grid> 

In the beginning of the XAML you have imported two namespaces - example and telerik. The example namespace imports all types from the current test project like RoutedEventHelper. The second namespace is telerik and it imports all telerik navigation controls like RadTabControl from the assembly Telerik.Windows.Controls.Navigation.

Later in the UserControl.Resources section you declare a custom item container style, which defines the header and the content templates of the tab items. The header template contains a grid control with the close button on the right side and a content control located on the left side. The routing events for the close button are enabled using your new attached property EnableRoutedClick which is set to True.

  • The last step of this example scenario is to open MainPage.xaml.cs and to:

  • Bind the tab control to an observable collection containing items of the custom type TabItemModel

  • Register the RadTabItem class handler for the routed event CloseTabEvent.

public partial class MainPage : UserControl 
{ 
    ObservableCollection<TabItemModel> tabItemsModel = new ObservableCollection<TabItemModel>(); 
    public MainPage() 
    { 
        InitializeComponent(); 
        this.CreateTabItems(); 
        EventManager.RegisterClassHandler( typeof(RadTabItem), RoutedEventHelper.CloseTabEvent, new RoutedEventHandler(OnCloseClicked)); 
    } 
    public void OnCloseClicked(object sender, RoutedEventArgs e) 
    { 
        var tabItem = sender as RadTabItem; 
        // Remove the item from the collection the control is bound to 
        tabItemsModel.Remove(tabItem.DataContext as TabItemModel); 
    } 
    private void CreateTabItems() 
    { 
        // Create items: 
        for (int num = 0; num < 5; num++) 
        { 
            tabItemsModel.Add(new TabItemModel() 
            { 
                Title = String.Format("Item {0}", num), 
                Content = String.Format("Item Content {0}", num) 
            }); 
        } 
        // Attach the items: 
        tabControl.ItemsSource = tabItemsModel; 
    } 
} 
public class TabItemModel 
{ 
    public String Title 
    { 
        get; 
        set; 
    } 
    public String Content 
    { 
        get; 
        set; 
    } 
} 
Partial Public Class Page 
    Inherits UserControl 
    Private tabItemsModel As New ObservableCollection(Of TabItemModel)() 
    Public Sub New() 
        InitializeComponent() 
        Me.CreateTabItems() 
        EventManager.RegisterClassHandler(GetType(RadTabItem), RoutedEventHelper.CloseTabEvent, New RoutedEventHandler(AddressOf OnCloseClicked)) 
    End Sub 
    Public Sub OnCloseClicked(ByVal sender As Object, ByVal e As RoutedEventArgs) 
        Dim tabItem = TryCast(sender, RadTabItem) 
        ' Remove the item from the collection the control is bound to 
        tabItemsModel.Remove(TryCast(tabItem.DataContext, TabItemModel)) 
    End Sub 
    Private Sub CreateTabItems() 
        ' Create items: 
        For num As Integer = 0 To 4 
            Dim itemModel = New TabItemModel() 
            itemModel.Content = String.Format("Item Content {0}", num) 
            itemModel.Title = String.Format("Item {0}", num) 
            tabItemsModel.Add(itemModel) 
        Next 
        ' Attach the items: 
        tabControl.ItemsSource = tabItemsModel 
    End Sub 
End Class 
Public Class TabItemModel 
    Private _Title As [String] 
    Public Property Title() As [String] 
        Get 
            Return _Title 
        End Get 
        Set(ByVal value As [String]) 
            _Title = value 
        End Set 
    End Property 
    Private _Content As [String] 
    Public Property Content() As [String] 
        Get 
            Return _Content 
        End Get 
        Set(ByVal value As [String]) 
            _Content = value 
        End Set 
    End Property 
End Class 

See Also

In this article