Edit this page

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, after navigating to TileView/AddCloseButton/Example.xaml.

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
    

You can download a runnable project of the demonstrated example from our online SDK repository here, after navigating to TileView/AddCloseButton.

See Also