Add a Close Button to a RadTileViewItem

This article demonstrates how to add a close button next to the tile state change (minimize/maximize) button of the RadTileViewItems. The Command property of the close button will be bound to an ICommand that removes the item from the RadTileView's ItemsSource.

You can see the final result on the picture bellow: radtileview-howto-add-close-button-to-radtileiewitem.aml 01

Define the RadTileView in XAML and create the view model

The RadTileView in this scenario will be data bound to a collection of business objects.

public class DataItem 
{ 
    public string Header { get; set; } 
    public string Content { get; set; } 
} 
Public Class DataItem 
    Public Property Header() As String 
        Get 
            Return m_Header 
        End Get 
        Set(value As String) 
            m_Header = Value 
        End Set 
    End Property 
    Private m_Header As String 
    Public Property Content() As String 
        Get 
            Return m_Content 
        End Get 
        Set(value As String) 
            m_Content = Value 
        End Set 
    End Property 
    Private m_Content As String 
End Class 

The collection passed to the RadTileView's ItemsSource can be wrapped in a View Model class

public class MainViewModel 
{ 
    public ObservableCollection<DataItem> Items { get; set; } 
 
    public MainViewModel() 
    { 
        this.Items = new ObservableCollection<DataItem>(); 
 
        for (int i = 0; i < 2; i++) 
        { 
            var dataItem = new DataItem() 
            { 
                Header = "Item " + i, 
                Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." 
            }; 
 
            this.Items.Add(dataItem); 
        } 
    } 
} 
Public Class MainViewModel 
    Public Property Items() As ObservableCollection(Of DataItem) 
        Get 
            Return m_Items 
        End Get 
        Set(value As ObservableCollection(Of DataItem)) 
            m_Items = Value 
        End Set 
    End Property 
    Private m_Items As ObservableCollection(Of DataItem) 
 
    Public Sub New() 
        Me.Items = New ObservableCollection(Of DataItem)() 
 
        For i As Integer = 0 To 4 
            Dim dataItem = New DataItem() With { _ 
                .Header = "Item " + i, _ 
                .Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." _ 
            } 
 
            Me.Items.Add(dataItem) 
        Next 
    End Sub 
End Class 

After creating the view models you can add the RadTileView definition in xaml.

<telerik:RadTileView ItemsSource="{Binding Items}"                    
                     MinimizedRowHeight="150"  
                     MinimizedColumnWidth="200"  
                     HeaderStyle="{StaticResource TileViewItemHeaderStyle1}"> 
    <telerik:RadTileView.DataContext> 
        <local:MainViewModel /> 
    </telerik:RadTileView.DataContext> 
    <telerik:RadTileView.ItemTemplate> 
        <DataTemplate> 
            <TextBlock Text="{Binding Header}" HorizontalAlignment="Right"/> 
        </DataTemplate> 
    </telerik:RadTileView.ItemTemplate> 
    <telerik:RadTileView.ContentTemplate> 
        <DataTemplate> 
            <TextBlock Margin="16" Text="{Binding Content}" TextWrapping="Wrap" /> 
        </DataTemplate> 
    </telerik:RadTileView.ContentTemplate> 
</telerik:RadTileView> 

Extract and Edit the RadTileViewItem HeaderStyle

The RadTileViewItem’s header is represented by TileViewItemHeader control. To add close button you can extract the Style of the TileViewItemHeader and slightly modify its ControlTemplate. Basically, you can find the RadToggleButton that represents the Minimize/Maximize button and wrap it in a Grid (or another panel) along with the new close button.

You can see the Editing Control Templates help article that demonstrates how to extract the Style with the ControlTemplate of a control.

................ 
<Border BorderBrush="White" BorderThickness="0,0,0,1" Background="{TemplateBinding Background}" CornerRadius="1" Padding="10,0,7,0"> 
<Grid MinHeight="28"> 
    <Border x:Name="GripBarElement" Background="Transparent"> 
        <ContentPresenter x:Name="HeaderElement" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0,0,10,0" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 
    </Border> 
 
    <Grid HorizontalAlignment="Right" VerticalAlignment="Center"> 
        <Grid.ColumnDefinitions> 
            <ColumnDefinition /> 
            <ColumnDefinition /> 
        </Grid.ColumnDefinitions> 
 
        <telerik:RadButton Margin="2 0 0 0" Command="local:TileViewCommandsExtension.Delete" Grid.Column="1" x:Name="CloseButton" InnerCornerRadius="0"> 
            <telerik:RadButton.Style> 
                <Style TargetType="{x:Type telerik:RadButton}"> 
                    <Setter Property="VerticalAlignment" Value="Center"/> 
                    <Setter Property="HorizontalAlignment" Value="Right"/> 
                    <Setter Property="Width" Value="17"/> 
                    <Setter Property="Height" Value="17"/> 
                    <Setter Property="Template"> 
                        <Setter.Value> 
                            <ControlTemplate TargetType="{x:Type telerik:RadButton}"> 
                                <Grid Background="Transparent" SnapsToDevicePixels="True"> 
                                    <VisualStateManager.VisualStateGroups> 
                                        <VisualStateGroup x:Name="CommonStates"> 
                                            <VisualState x:Name="Disabled"/> 
                                            <VisualState x:Name="Normal"/> 
                                            <VisualState x:Name="MouseOver"> 
                                                <Storyboard> 
                                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="closeButtonPath"> 
                                                        <DiscreteObjectKeyFrame KeyTime="0"> 
                                                            <DiscreteObjectKeyFrame.Value> 
                                                                <SolidColorBrush Color="White" /> 
                                                            </DiscreteObjectKeyFrame.Value> 
                                                        </DiscreteObjectKeyFrame> 
                                                    </ObjectAnimationUsingKeyFrames> 
                                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="closeButtonPath"> 
                                                        <DiscreteObjectKeyFrame KeyTime="0"> 
                                                            <DiscreteObjectKeyFrame.Value> 
                                                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> 
                                                                    <GradientStop Color="#FFFEEDB7" Offset="0"/> 
                                                                    <GradientStop Color="#FFFACA6A" Offset="1"/> 
                                                                    <GradientStop Color="#FFFFC94A" Offset="0.526"/> 
                                                                    <GradientStop Color="#FFFEEDB7" Offset="0.509"/> 
                                                                </LinearGradientBrush> 
 
                                                            </DiscreteObjectKeyFrame.Value> 
                                                        </DiscreteObjectKeyFrame> 
                                                    </ObjectAnimationUsingKeyFrames> 
                                                </Storyboard> 
                                            </VisualState> 
                                        </VisualStateGroup> 
                                    </VisualStateManager.VisualStateGroups> 
 
                                    <Grid x:Name="closeButtonContent">                                       
                                        <Path x:Name="closeButtonPath" Stretch="Uniform" 
                                              Stroke="White" 
                                              StrokeThickness="1.5" 
                                              VerticalAlignment="Center"  
                                              Height="15" Width="15" 
                                              Data="M41.191966,0.5 L71.431967,0.5 71.431967,41.207996 112.12394,42.350311 111.27536,72.578407 71.431967,71.459915 71.431967,111.319 41.191966,111.319 41.191966,70.611007 0.5,69.468688 1.3485718,39.240596 41.191966,40.359092 z"> 
                                            <Path.LayoutTransform> 
                                                <TransformGroup> 
                                                    <ScaleTransform ScaleX="-1"/> 
                                                    <SkewTransform/> 
                                                    <RotateTransform Angle="45.01"/> 
                                                    <TranslateTransform/> 
                                                </TransformGroup> 
                                            </Path.LayoutTransform> 
 
                                            <Path.Fill> 
                                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> 
                                                    <GradientStop Color="#FF282828"/> 
                                                    <GradientStop Color="#FF7C7C7C" Offset="1"/> 
                                                </LinearGradientBrush> 
                                            </Path.Fill> 
                                        </Path> 
                                    </Grid> 
                                </Grid> 
                            </ControlTemplate> 
                        </Setter.Value> 
                    </Setter> 
                </Style> 
            </telerik:RadButton.Style> 
        </telerik:RadButton> 
 
        <telerik:RadToggleButton x:Name="MaximizeToggleButton" Command="TileView:TileViewCommands.ToggleTileState" InnerCornerRadius="0"> 
            ................. 
        </telerik:RadToggleButton> 
    </Grid>  
</Grid> 
</Border> 
........... 

The code snippet above doesn't contain the complete definition of the TileViewItemHeader.ControlTemplate, but only the part of it that is changed. You can see the complete template in our GitHub SDK repository.

Implement the Close button’s command

The final stage of this tutorial is to implement the Close button’s command. We will use a static class holding an RoutedUICommand and then we will implement its behavior by adding command binding for it. To do so, you can follow the next steps:

  1. Create a static class that holds the command.

        public static class TileViewCommandsExtension 
        { 
            static TileViewCommandsExtension() 
            { 
                TileViewCommandsExtension.Delete = new RoutedUICommand("Deletes a tile view item", "Delete", typeof(TileViewCommandsExtension)); 
            } 
     
            public static RoutedUICommand Delete { get; private set; } 
        } 
    
        Public NotInheritable Class TileViewCommandsExtension 
            Private Sub New() 
            End Sub 
            Shared Sub New() 
                TileViewCommandsExtension.Delete = New RoutedUICommand("Deletes a tile view item", "Delete", GetType(TileViewCommandsExtension)) 
            End Sub 
     
            Public Shared Property Delete() As RoutedUICommand 
                Get 
                    Return m_Delete 
                End Get 
                Private Set(value As RoutedUICommand) 
                    m_Delete = Value 
                End Set 
            End Property 
            Private Shared m_Delete As RoutedUICommand 
        End Class 
    
  2. Set the Command property of the close RadButton in the ControlTemplate of the TileViewItemHeader to the command.

        <telerik:RadButton Margin="2 0 0 0" Command="local:TileViewCommandsExtension.Delete" Grid.Column="1" x:Name="CloseButton" InnerCornerRadius="0"> 
    
  3. Register command binding for the command in the static constructor of the UserControl where the RadTileView is used.

        public partial class Example : UserControl 
        { 
            static Example() 
            { 
                var deleteBinding = new CommandBinding(TileViewCommandsExtension.Delete, OnDeleteCommandExecute, OnCanDeleteCommandExecute); 
                CommandManager.RegisterClassCommandBinding(typeof(RadTileViewItem), deleteBinding); 
            } 
     
            private static void OnCanDeleteCommandExecute(object sender, CanExecuteRoutedEventArgs e) 
            {             
            } 
     
            private static void OnDeleteCommandExecute(object sender, ExecutedRoutedEventArgs e) 
            { 
            } 
     
            public Example() 
            { 
                InitializeComponent(); 
            } 
        } 
    
        Partial Public Class Example 
            Inherits UserControl 
            Shared Sub New() 
                Dim deleteBinding = New CommandBinding(TileViewCommandsExtension.Delete, AddressOf OnDeleteCommandExecute, AddressOf OnCanDeleteCommandExecute) 
                CommandManager.RegisterClassCommandBinding(GetType(RadTileViewItem), deleteBinding) 
            End Sub 
     
            Private Shared Sub OnCanDeleteCommandExecute(sender As Object, e As CanExecuteRoutedEventArgs) 
            End Sub 
     
            Private Shared Sub OnDeleteCommandExecute(sender As Object, e As ExecutedRoutedEventArgs) 
            End Sub 
     
            Public Sub New() 
                InitializeComponent() 
            End Sub 
        End Class 
    
  4. Define behavior for the command by implementing the Execute and CanExecute event handlers.

        public partial class Example : UserControl 
        { 
            static Example() 
            { 
                var deleteBinding = new CommandBinding(TileViewCommandsExtension.Delete, OnDeleteCommandExecute, OnCanDeleteCommandExecute); 
                CommandManager.RegisterClassCommandBinding(typeof(RadTileViewItem), deleteBinding); 
            } 
     
            private static void OnCanDeleteCommandExecute(object sender, CanExecuteRoutedEventArgs e) 
            { 
                e.CanExecute = true; 
            } 
     
            private static void OnDeleteCommandExecute(object sender, ExecutedRoutedEventArgs e) 
            { 
                var tileViewItem = sender as RadTileViewItem; 
                var tileView = tileViewItem.ParentTileView as RadTileView; 
                if (tileViewItem == null || tileView == null) return; 
     
                if (tileView.ItemsSource != null) 
                { 
                    var dataItem = tileView.ItemContainerGenerator.ItemFromContainer(tileViewItem) as DataItem; 
     
                    // Note: This will change the DataContext's Items collection. 
                    var source = tileView.ItemsSource as IList; 
                    if (dataItem != null && source != null) 
                        source.Remove(dataItem); 
                } 
                else 
                { 
                    tileView.Items.Remove(tileViewItem); 
                } 
            } 
     
            public Example() 
            { 
                InitializeComponent(); 
            } 
        } 
    
        Partial Public Class Example 
            Inherits UserControl 
            Shared Sub New() 
                Dim deleteBinding = New CommandBinding(TileViewCommandsExtension.Delete, AddressOf OnDeleteCommandExecute, AddressOf OnCanDeleteCommandExecute) 
                CommandManager.RegisterClassCommandBinding(GetType(RadTileViewItem), deleteBinding) 
            End Sub 
     
            Private Shared Sub OnCanDeleteCommandExecute(sender As Object, e As CanExecuteRoutedEventArgs) 
                e.CanExecute = True 
            End Sub 
     
            Private Shared Sub OnDeleteCommandExecute(sender As Object, e As ExecutedRoutedEventArgs) 
                Dim tileViewItem = TryCast(sender, RadTileViewItem) 
                Dim tileView = TryCast(tileViewItem.ParentTileView, RadTileView) 
                If tileViewItem Is Nothing OrElse tileView Is Nothing Then 
                    Return 
                End If 
     
                If tileView.ItemsSource IsNot Nothing Then 
                    Dim dataItem = TryCast(tileView.ItemContainerGenerator.ItemFromContainer(tileViewItem), DataItem) 
     
                    ' Note: This will change the DataContext's Items collection. 
                    Dim source = TryCast(tileView.ItemsSource, IList) 
                    If dataItem IsNot Nothing AndAlso source IsNot Nothing Then 
                        source.Remove(dataItem) 
                    End If 
                Else 
                    tileView.Items.Remove(tileViewItem) 
                End If 
            End Sub 
     
            Public Sub New() 
                InitializeComponent() 
            End Sub 
        End Class 
    

Find a runnable project of the previous example in the WPF Samples GitHub repository.

See Also

In this article