Telerik UI for Windows Phone by Progress

This help topic describes how to use the grouping capability of RadConversationView to create a chat simulation in which each sequence of messages from one person is displayed under the same group.

Grouping Messages

The first thing that needs to be done is to declare RadConversationView in XAML:

CopyXAML
<telerikDataControls:RadConversationView x:Name="conversationView"
                                         Grid.Row="1"
                                         GroupHeaderTemplateSelector="{StaticResource GroupTemplateSelector}"/>
Next we need to create our group template selector. It will display an image to the left for outgoing messages and a different image to the right for incoming messages. Our selector will keep track of the type of the previous message and will assign unique categories when appropriate. That is, whenever the added message's type differs from the previous message's type:
CopyC#
public class GroupTemplateSelector : DataTemplateSelector
{
    private ConversationViewMessage previousMessage;
    private int uniqueGroupIdentifier = 0;
    private ConversationViewMessageType currentType = ConversationViewMessageType.Outgoing;

    public DataTemplate IncomingTemplate
    {
        get;
        set;
    }

    public DataTemplate OutgoingTemplate
    {
        get;
        set;
    }

    public void OnMessageAdded(CustomConversationViewMessage message)
    {
        if (previousMessage != null && previousMessage.Type != message.Type)
        {
            // Generate new group.
            message.Group = ++this.uniqueGroupIdentifier;

            if (previousMessage.Type == ConversationViewMessageType.Outgoing)
            {
                this.currentType = ConversationViewMessageType.Incoming;
            }
            else
            {
                this.currentType = ConversationViewMessageType.Outgoing;
            }
        }
        else
        {
            // Add message to current group
            message.Group = this.uniqueGroupIdentifier;
        }

        this.previousMessage = message;
    }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        switch (this.currentType)
        {
            case ConversationViewMessageType.Incoming:
                return this.IncomingTemplate;
            case ConversationViewMessageType.Outgoing:
                return this.OutgoingTemplate;
        }

        return null;
    }
}
We have to make sure that we generate correct groups as messages arrive. Groups will be added dynamically because we need unique groups for every new sequence of messages from a unique person. For example, if we only add groups once for incoming and outgoing messages, all messages will be in only two groups and the messages will not be ordered. They will be ordered in their respective groups but not as a whole. We generate a new group each time a message arrives and when its type is different from the previous message. Time for our two group templates. They are pretty simple:
CopyXAML
<Grid.Resources>
    <local:GroupTemplateSelector x:Key="GroupTemplateSelector">
        <local:GroupTemplateSelector.IncomingTemplate>
            <DataTemplate>
                <Grid Margin="12">
                    <Image HorizontalAlignment="Right"
                           Source="Charlize.jpg"
                           Stretch="UniformToFill"
                           Width="100"
                           Height="100"/>
                </Grid>
            </DataTemplate>
            </local:GroupTemplateSelector.IncomingTemplate>

        <local:GroupTemplateSelector.OutgoingTemplate>
            <DataTemplate>
                <Grid Margin="12">
                    <Image HorizontalAlignment="Left"
                           Source="Wikus.png"
                           Width="100"
                           Height="100"/>
                </Grid>
            </DataTemplate>
            </local:GroupTemplateSelector.OutgoingTemplate>
        </local:GroupTemplateSelector>
</Grid.Resources>
Finally here is the code in main page the glues together the selector and the incoming\outgoning messages:
CopyC#
public partial class MainPage : PhoneApplicationPage
{
    IEnumerator<CustomConversationViewMessage> sourceEnumerator;
    GroupTemplateSelector selector;

    private List<CustomConversationViewMessage> messageSource = new List<CustomConversationViewMessage>()
    { 
        new CustomConversationViewMessage("Hello Charlize!", DateTime.Now, ConversationViewMessageType.Outgoing),
        new CustomConversationViewMessage("are you there?", DateTime.Now.AddSeconds(3), ConversationViewMessageType.Outgoing),
        new CustomConversationViewMessage("Yes", DateTime.Now.AddSeconds(7), ConversationViewMessageType.Incoming),
        new CustomConversationViewMessage("Who's this?", DateTime.Now.AddSeconds(12), ConversationViewMessageType.Incoming),
        new CustomConversationViewMessage("This is Wikus van der Merwe!", DateTime.Now.AddSeconds(16), ConversationViewMessageType.Outgoing),
        new CustomConversationViewMessage("From the funny flick District 9!", DateTime.Now.AddSeconds(23), ConversationViewMessageType.Outgoing)
    };

    private ObservableCollection<CustomConversationViewMessage> messages = new ObservableCollection<CustomConversationViewMessage>();

    public MainPage()
    {
        InitializeComponent();
        this.selector = this.LayoutRoot.Resources["GroupTemplateSelector"] as GroupTemplateSelector;

        sourceEnumerator = messageSource.GetEnumerator();
        messages.CollectionChanged += this.OnMessagesChanged;
        this.conversationView.ItemsSource = messages;
        this.conversationView.GroupDescriptors = new DataDescriptor[] { new GenericGroupDescriptor<CustomConversationViewMessage, int>((message) => message.Group) };
    }

    private void OnMessagesChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems == null)
        {
            return;
        }

        CustomConversationViewMessage message = e.NewItems[0] as CustomConversationViewMessage;

        this.selector.OnMessageAdded(message);
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (sourceEnumerator.MoveNext())
        {
            this.messages.Add(sourceEnumerator.Current);
        }
    }
}
It should be noted that we are using a custom message here, not the default ConversationViewMessage. The new CustomConversationViewMessage is pretty simple, it just inherits from ConversationViewMessage and adds a new Group property of type int(see code) that has a getter and setter so that it can be manipulated from the GroupTemplateSelector. Whenever we receive a new message, if it's type is different from the last message, we generate a new group and add new the message to the new group, otherwise, we just add the new message to the last group. Here is the result:
radconversationview-groupingmessages