New to Telerik UI for Xamarin? Download free 30-day trial

ItemTemplateSelector

RadChat control exposes an ItemTemplateSelector property which you can use to apply different templates to each chat item depending on a specific condition.

Default ItemTemplateSelector

Any change on the appearance of the chat items depends on the ChatItemTemplateSelector and the containing templates and referenced Styles. The default selector includes separate templates for the incoming and outgoing messages (so they're aligned on the left/right accordingly), as well as for single and the first, middle and last messages (in case there area a few messages in a row) - this is needed in order to achieve the "balloon" look & feel of the messages. In addition, the TimeBreak template is also assigned through the ItemTemplateSelector.

Below you can find the default ItemTemplateSelector which you can use as a base for any further customizations to the way the messages look.

In short, the default templates contain RadBorder (used to achieve the rounded edges), image control (used for the avatar image) only for the single and first messages, and a Label for the text message itself.

The snippet below contains the default templates and the accompanying styles:

<ResourceDictionary>
    <Style x:Key="MessageImageStyle" TargetType="Image">
        <Setter Property="WidthRequest" Value="24" />
        <Setter Property="HeightRequest" Value="24" />
        <Setter Property="VerticalOptions" Value="Start" />
    </Style>

    <Style x:Key="OutgoingMessageImageStyle" TargetType="Image" BasedOn="{StaticResource MessageImageStyle}">
        <Setter Property="HorizontalOptions" Value="End" />
        <Setter Property="Margin" Value="0, 4, 10, 4" />
    </Style>

    <Style x:Key="IncomingMessageImageStyle" TargetType="Image" BasedOn="{StaticResource MessageImageStyle}">
        <Setter Property="HorizontalOptions" Value="Start" />
        <Setter Property="Margin" Value="10, 4, 0, 4" />
    </Style>

    <Style x:Key="IncomingBorderStyle" TargetType="telerikPrimitives:RadBorder">
        <Setter Property="BackgroundColor" Value="#FFFFFF" />                
        <Setter Property="Margin" Value="45, 0, 70, 0" />
        <Setter Property="HorizontalOptions" Value="Start" />
    </Style>

    <Style x:Key="OutgoingBorderStyle" TargetType="telerikPrimitives:RadBorder">
        <Setter Property="BackgroundColor" Value="#E0E0E0" />
        <Setter Property="Margin" Value="70, 0, 45, 0" />
        <Setter Property="HorizontalOptions" Value="End" />
    </Style>

    <Style x:Key="DefaultLabelStyle" TargetType="Label">
        <Setter Property="Margin" Value="20, 5, 20, 5" />
        <Setter Property="FontSize" Value="16" />
        <Setter Property="LineBreakMode" Value="WordWrap" />
        <Setter Property="TextColor" Value="Black" />
        <Setter Property="HorizontalOptions" Value="Start" />
    </Style>

    <Style x:Key="OutgoingLabelStyle" TargetType="Label" BasedOn="{StaticResource DefaultLabelStyle}">
        <Setter Property="HorizontalOptions" Value="End" />
    </Style>

    <DataTemplate x:Key="OutgoingSingleTemplate">
        <Grid Padding="0, 2, 0, 10">
            <Image Source="{Binding Author.Avatar}" Style="{StaticResource OutgoingMessageImageStyle}" />
            <telerikPrimitives:RadBorder Style="{StaticResource OutgoingBorderStyle}"
                                         CornerRadius="7, 0, 7, 7" >
                <Label Text="{Binding Text}" Style="{StaticResource OutgoingLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="OutgoingFirstTemplate">
        <Grid Padding="0, 2, 0, 2">
            <Image Source="{Binding Author.Avatar}" 
                   Style="{StaticResource OutgoingMessageImageStyle}" />
            <telerikPrimitives:RadBorder Style="{StaticResource OutgoingBorderStyle}"
                                         CornerRadius="7, 0, 0, 7" >
                <Label Text="{Binding Text}" Style="{StaticResource OutgoingLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="OutgoingMiddleTemplate">
        <Grid Padding="0, 2, 0, 2">
            <telerikPrimitives:RadBorder Style="{StaticResource OutgoingBorderStyle}"
                                         CornerRadius="7, 0, 0, 7" >
                <Label Text="{Binding Text}" Style="{StaticResource OutgoingLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="OutgoingLastTemplate">
        <Grid Padding="0, 2, 0, 10">
            <telerikPrimitives:RadBorder Style="{StaticResource OutgoingBorderStyle}"
                                         CornerRadius="7, 0, 7, 7" >
                <Label Text="{Binding Text}" Style="{StaticResource OutgoingLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="IncomingSingleTemplate">
        <Grid Padding="0, 2, 0, 10">
            <Image Source="{Binding Author.Avatar}" 
                   Style="{StaticResource IncomingMessageImageStyle}" />
            <telerikPrimitives:RadBorder Style="{StaticResource IncomingBorderStyle}"
                                         CornerRadius="0, 7, 7, 7" >
                <Label Text="{Binding Text}" Style="{StaticResource DefaultLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="IncomingFirstTemplate">
        <Grid Padding="0, 2, 0, 2">
            <Image Source="{Binding Author.Avatar}" 
                   Style="{StaticResource IncomingMessageImageStyle}" />
            <telerikPrimitives:RadBorder Style="{StaticResource IncomingBorderStyle}"
                                         CornerRadius="0, 7, 7, 0" >
                <Label Text="{Binding Text}" Style="{StaticResource DefaultLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="IncomingMiddleTemplate">
        <Grid Padding="0, 2, 0, 2">
            <telerikPrimitives:RadBorder Style="{StaticResource IncomingBorderStyle}"
                                         CornerRadius="0, 7, 7, 0" >
                <Label Text="{Binding Text}" Style="{StaticResource DefaultLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="IncomingLastTemplate">
        <Grid Padding="0, 2, 0, 10">
            <telerikPrimitives:RadBorder Style="{StaticResource IncomingBorderStyle}"
                                         CornerRadius="0, 7, 7, 7" >
                <Label Text="{Binding Text}" Style="{StaticResource DefaultLabelStyle}" />
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>

    <ControlTemplate x:Key="TimeBreakView_ControlTemplate">
        <Grid Padding="10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <BoxView BackgroundColor="{TemplateBinding Stroke}"
             HeightRequest="{TemplateBinding StrokeThickness}"
             VerticalOptions="Center" />
            <Label Text="{TemplateBinding Text}" 
                   TextColor="{TemplateBinding TextColor}" 
                   FontSize="{TemplateBinding FontSize}" 
                   FontFamily="{TemplateBinding FontFamily}" 
                   FontAttributes="{TemplateBinding FontAttributes}" 
                   Grid.Column="1"
                   VerticalOptions="Center" />
            <BoxView BackgroundColor="{TemplateBinding Stroke}"
                     HeightRequest="{TemplateBinding StrokeThickness}"
                     Grid.Column="2"
                     VerticalOptions="Center" />
        </Grid>
    </ControlTemplate>

    <DataTemplate x:Key="TimeBreakTemplate">
        <telerikConversationalUI:TimeBreakView Text="{Binding Text}" 
                                               ControlTemplate="{StaticResource TimeBreakView_ControlTemplate}" />
    </DataTemplate>

    <telerikConversationalUI:ChatItemTemplateSelector x:Key="MyChatItemTemplateSelector"
                                                      IncomingFirstTextMessageTemplate="{StaticResource IncomingFirstTemplate}"
                                                      IncomingMiddleTextMessageTemplate="{StaticResource IncomingMiddleTemplate}"
                                                      IncomingSingleTextMessageTemplate="{StaticResource IncomingSingleTemplate}"
                                                      IncomingLastTextMessageTemplate="{StaticResource IncomingLastTemplate}"
                                                      OutgoingFirstTextMessageTemplate="{StaticResource OutgoingFirstTemplate}"
                                                      OutgoingMiddleTextMessageTemplate="{StaticResource OutgoingMiddleTemplate}"
                                                      OutgoingSingleTextMessageTemplate="{StaticResource OutgoingSingleTemplate}"
                                                      OutgoingLastTextMessageTemplate="{StaticResource OutgoingLastTemplate}"
                                                      TimeBreakTemplate="{StaticResource TimeBreakTemplate}" />
</ResourceDictionary>

You can make any changes to the templates and then assign the template selector to the ItemTemplateSelector property of the Chat control:

<telerikConversationalUI:RadChat x:Name="chat"                                                  
                                 ItemTemplateSelector="{StaticResource MyChatItemTemplateSelector}" />

Custom ItemTemplateSelector

You can also create a custom ChatItemTemplateSelector to conditionally apply different messages styles depending on any of the used chat item properties.

Let's, for example, have the following ChatItem class with a custom MessageCategory property to distinguish important messages:

public enum MessageCategory
{
    Important,
    Normal
}
public class SimpleChatItem
{
    public Author Author { get; set; }
    public string Text { get; set; }
    public MessageCategory Category { get; set; }
}

Add a few sample Items to the Chat's ItemsSource:

public class ViewModel : NotifyPropertyChangedBase
{
    private Author me;
    public ViewModel()
    {          
       string prefix = Device.RuntimePlatform == Device.UWP ? "Assets/" : null;

        this.Me = new Author() { Name = "human", Avatar = prefix + "sampleAvatar.png" };
        this.Bot = new Author() { Name = "Bot", Avatar = prefix + "sampleBot.png" };
        this.Items = new ObservableCollection<SimpleChatItem>();

        // Simulate async data loading
        Device.StartTimer(TimeSpan.FromMilliseconds(500), () =>
        {
            this.Items.Add(new SimpleChatItem { Author = this.Bot, Text = "Hi.", Category = MessageCategory.Normal });
            this.Items.Add(new SimpleChatItem { Author = this.Bot, Text = "Please check our new privacy policy here:...", Category = MessageCategory.Important });
            return false;
        });
    }

    public Author Me
    {
        get
        {
            return this.me;
        }
        set
        {
            if (this.me != value)
            {
                this.me = value;
                this.OnPropertyChanged(nameof(this.Me));
            }
        }
    }
    public Author Bot { get; set; }
    public IList<SimpleChatItem> Items { get; set; }
}

You would need to supply an ItemsConverter as you're using custom items as demonstrated inside MVVM Support topic.

public class SimpleChatItemConverter : IChatItemConverter
{
    public ChatItem ConvertToChatItem(object dataItem, ChatItemConverterContext context)
    {
        SimpleChatItem item = (SimpleChatItem)dataItem;
        TextMessage textMessage = new TextMessage()
        {
            Data = dataItem,
            Author = item.Author,
            Text = item.Text
        };
        return textMessage;
    }
    public object ConvertToDataItem(object message, ChatItemConverterContext context)
    {
        ViewModel vm = (ViewModel)context.Chat.BindingContext;
        return new SimpleChatItem { Author = vm.Me, Text = (string)message, Category=MessageCategory.Normal };
    }
}

Create a CustomChatItemTemplateSelector class that derives from the ChatItemTemplateSelector:

public class CustomChatItemTemplateSelector : ChatItemTemplateSelector
{
    public DataTemplate ImportantMessageTemplate { get; set; }

    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        ChatItem chatItem = item as ChatItem;
        var myItem = chatItem?.Data as SimpleChatItem;
        if (myItem != null && myItem.Category == MessageCategory.Important)
        {
            return this.ImportantMessageTemplate;
        }
        return base.OnSelectTemplate(item, container);
    }
}

Create the needed XAML resources:

<ResourceDictionary>
    <local:SimpleChatItemConverter x:Key="SimpleChatItemConverter" />

    <Style x:Key="MessageImageStyle" TargetType="Image">
        <Setter Property="Source" Value="{Binding Author.Avatar}" />
        <Setter Property="WidthRequest" Value="30" />
        <Setter Property="HeightRequest" Value="30" />
    </Style>

    <Style x:Key="IncomingMessageImageStyle" TargetType="Image" BasedOn="{StaticResource MessageImageStyle}">
        <Setter Property="HorizontalOptions" Value="Start" />
        <Setter Property="Margin" Value="10, 0, 0, 0" />
    </Style>

    <DataTemplate x:Key="ImportantMessageTemplate">
        <Grid Margin="0, 2, 0, 10">
            <Image Style="{StaticResource IncomingMessageImageStyle}" />
            <telerikPrimitives:RadBorder CornerRadius="0, 7, 7, 7"
                                    Margin="45, 0, 50, 0"
                                    HorizontalOptions="Start"
                                    BackgroundColor="#FF0000">
                <StackLayout Orientation="Horizontal" Margin="20, 0, 20, 0">
                    <Label Text="! " FontAttributes="Bold" FontSize="Medium" />
                    <Label Text="{Binding Text}" FontSize="Medium" />
                </StackLayout>
            </telerikPrimitives:RadBorder>
        </Grid>
    </DataTemplate>
    <local:CustomChatItemTemplateSelector x:Key="CustomChatItemTemplateSelector"
        ImportantMessageTemplate="{StaticResource ImportantMessageTemplate}" />
</ResourceDictionary>

Set it to the Chat's ItemTemplateSelector property:

<telerikConversationalUI:RadChat x:Name="chat"                                    
                    Author="{Binding Me}"
                    ItemsSource="{Binding Items}" 
                    ItemConverter="{StaticResource SimpleChatItemConverter}"                          
                    ItemTemplateSelector="{StaticResource CustomChatItemTemplateSelector}">
    <telerikConversationalUI:RadChat.BindingContext>
        <local:ViewModel />
    </telerikConversationalUI:RadChat.BindingContext>
</telerikConversationalUI:RadChat>

and add the telerikPrimitives namespace:

xmlns:telerikPrimitives="clr-namespace:Telerik.XamarinForms.Primitives;assembly=Telerik.XamarinForms.Primitives"

Figure 1: RadChat with ItemTemplateSelector

ItemTemplateSelector

See Also

In this article