I have a scenario where I have "dynamic" content in a listview. I am making a sort of chat application where you can send hyperlinks, images, videos, etc.
I have a template defined in XAML for an incoming message and outgoing message, but I need to be able to say "this message has an image", "this message has a link", "this message has a text body with a link in the middle of it", "this message has both text and 2 images", etc.
How can I dynamically set what content is in each listview item without having to define a bunch of things that are IsVisible="true/false"?
3 Answers, 1 is accepted
0
Hi Alex,
Dynamically updating an existing item's content after it's been added is not really the best way to go. Instead you would have already decided what type of item will be added and choose the appropriate template for that message using the TemplateSelector
In the documentaiotn, you'll see different types of classes for message. For example, we ship TextMessage out of the box because it's a guranteed message type across the available platforms.,
The control was explicitly designed as platform and model agnostic so that you have the flexibility to define the Message class that will be used for the service. It's the ItemTemplateSelector determines what template to use in the list dependeing on what kind of message it is.
I recommend reviewing our four main examples to gain a better understanding of how this works. A good place to start is the TravelAssistant example because it has a Template selector that returns a SummaryTemplate, FlightTemplate or WaitingForBotTemplate depending on the ChatItem's content.
In your case you could define a ImageMessageTemplate, VideoMessageTemplate, LinkMessageTemplate and so forth. As the items is pulled in from the data source, you explicitly put it in the backing collection as that message type.
Additional Guidance with Demo
You can extend TextMessage class and add your own properties to distinguish a particular message type from another. This way you can have custom templates for everythign and return the appropriate one.
Here's a simple example in which I extend TextMessage with special properties to let me know it's a ImageMessage, a WebUrlMessage.
When the data comes in from the service, you instantiate the appropriate message type for that item. In the example below I'm just randomly choosing the message type, but you should be able to determine what type of message it is at that type because of the incoming content.
Now you can define the template selector, using the Type of the message to determine which template to return:
Finally, here's what all of this comes together as in the UI:
and here's what that looks like at runtime:

Regards,
Lance | Technical Support Engineer, Principal
Progress Telerik
Dynamically updating an existing item's content after it's been added is not really the best way to go. Instead you would have already decided what type of item will be added and choose the appropriate template for that message using the TemplateSelector
In the documentaiotn, you'll see different types of classes for message. For example, we ship TextMessage out of the box because it's a guranteed message type across the available platforms.,
The control was explicitly designed as platform and model agnostic so that you have the flexibility to define the Message class that will be used for the service. It's the ItemTemplateSelector determines what template to use in the list dependeing on what kind of message it is.
I recommend reviewing our four main examples to gain a better understanding of how this works. A good place to start is the TravelAssistant example because it has a Template selector that returns a SummaryTemplate, FlightTemplate or WaitingForBotTemplate depending on the ChatItem's content.
In your case you could define a ImageMessageTemplate, VideoMessageTemplate, LinkMessageTemplate and so forth. As the items is pulled in from the data source, you explicitly put it in the backing collection as that message type.
Additional Guidance with Demo
You can extend TextMessage class and add your own properties to distinguish a particular message type from another. This way you can have custom templates for everythign and return the appropriate one.
Here's a simple example in which I extend TextMessage with special properties to let me know it's a ImageMessage, a WebUrlMessage.
public
class
ImageMessage : TextMessage
{
public
string
ImageUrl {
get
;
set
; }
}
public
class
WebUrlMessage : TextMessage
{
public
string
WebUrl {
get
;
set
; }
}
When the data comes in from the service, you instantiate the appropriate message type for that item. In the example below I'm just randomly choosing the message type, but you should be able to determine what type of message it is at that type because of the incoming content.
private
void
OnBotMessageReceived(
string
message)
{
Device.BeginInvokeOnMainThread(() =>
{
// I'm just randomly choosing the message type, but you should
// be able to determine which class to instantiate depending on your incoming content
var nextRandom = rand.Next(1, 100);
if
(nextRandom % 3 == 0)
{
chat.Items.Add(
new
WebUrlMessage
{
Author = botAuthor,
Text =
"Hey, check out this website!"
,
WebUrl =
"https://www.telerik.com"
});
}
else
if
(nextRandom % 2 == 0)
{
chat.Items.Add(
new
ImageMessage
{
Author = botAuthor,
Text =
"Hey, check out this photo!"
,
});
}
else
{
chat.Items.Add(
new
TextMessage
{
Author = botAuthor,
Text = $
"I know you said {message}, but where are you going for breakfast?"
,
});
}
});
}
Now you can define the template selector, using the Type of the message to determine which template to return:
public
class
MyChatItemTemplateSelector : ChatItemTemplateSelector
{
public
DataTemplate NormalTemplate {
get
;
set
; }
public
DataTemplate ImageTemplate {
get
;
set
; }
public
DataTemplate WebTemplate {
get
;
set
; }
protected
override
DataTemplate OnSelectTemplate(
object
item, BindableObject container)
{
if
(item
is
ImageMessage)
{
return
ImageTemplate;
}
else
if
(item
is
WebUrlMessage)
{
return
WebTemplate;
}
else
if
(item
is
TextMessage)
// make sure TextMessage is last because all other inherit from this
{
return
NormalTemplate;
}
return
base
.OnSelectTemplate(item, container);
}
}
Finally, here's what all of this comes together as in the UI:
<
ContentPage
...>
<
ContentPage.Resources
>
<
DataTemplate
x:Key
=
"MyNormalMessageTemplate"
>
<
StackLayout
>
<
Label
Text
=
"{Binding Text}"
/>
</
StackLayout
>
</
DataTemplate
>
<
DataTemplate
x:Key
=
"MyImageMessageTemplate"
>
<
StackLayout
>
<
Label
Text
=
"{Binding Text}"
/>
<
Image
Source
=
"{Binding ImageUrl}"
HeightRequest
=
"200"
/>
</
StackLayout
>
</
DataTemplate
>
<
DataTemplate
x:Key
=
"MyWebUrlMessageTemplate"
>
<
StackLayout
>
<
Label
Text
=
"{Binding Text}"
/>
<
WebView
Source
=
"{Binding WebUrl}"
HeightRequest
=
"400"
/>
</
StackLayout
>
</
DataTemplate
>
<
portable:MyChatItemTemplateSelector
x:Key
=
"MyMessageTemplateSelector"
NormalTemplate
=
"{StaticResource MyNormalMessageTemplate}"
ImageTemplate
=
"{StaticResource MyImageMessageTemplate}"
WebTemplate
=
"{StaticResource MyWebUrlMessageTemplate}"
/>
</
ContentPage.Resources
>
<
Grid
>
<
telerikConversationalUI:RadChat
x:Name
=
"chat"
ItemTemplateSelector
=
"{StaticResource MyMessageTemplateSelector}"
/>
</
Grid
>
</
ContentPage
>
and here's what that looks like at runtime:
Regards,
Lance | Technical Support Engineer, Principal
Progress Telerik
Do you want to have your say when we set our development plans?
Do you want to know when a feature you care about is added or when a bug fixed?
Explore the
Telerik Feedback Portal
and vote to affect the priority of the items
0

Korstiaan
Top achievements
Rank 1
answered on 21 Jun 2019, 05:32 AM
So what if hypothetically the message could change from one type to another? (e.g.you've send a message to someone (like a chat app) and have received a server message that the message's been read)
The most logical thing in my opinion would be to simply bind a property to an icon's visibility (icon instead of a background color for added complexity on the layout side) instead of adding another template with only 1 added element.
I've experienced that the radlistview does not always show the changes in visibility of elements when using a single template.
If I were to use multiple itemplates, will the radlistview then automatically reload and show the newly correct template?
Can't find any documentation explaining if that's the case.
The most logical thing in my opinion would be to simply bind a property to an icon's visibility (icon instead of a background color for added complexity on the layout side) instead of adding another template with only 1 added element.
I've experienced that the radlistview does not always show the changes in visibility of elements when using a single template.
If I were to use multiple itemplates, will the radlistview then automatically reload and show the newly correct template?
Can't find any documentation explaining if that's the case.
0
Hi Korstiaan,
You're correct, TemplateSelectors by design do not listen for property changes and the templates do not get switched after it's been rendered. If you wanted to trigger the TemplateSelector to render an item with a different template, you would remove and re-add the item.
The resulting CollectionChanged event will trigger the TemplateSelector because a DataTemplate is needed for the item changes
Single DataTemplate Option
You've described the other good option with using a single item template. you can have a single DataTemplate that has UI elements for the different types of messages and show/hide/change parts of that template using properties of the message data model.
Using the same example from my earlier post, you can combine both ImageUrl and WebUrl in the same model and use a converter to show or hide the
and the converter looks something like this:
You could use a single DataTemplate like this:
The same concept applies for anything else, like BackgroundColor, showing/hiding icons, etc. The only thing you really need to be sure of is that you're invoking property changed in some way when that message gets updated.
For example, if you're just trying to update the values of a message that already exists, you can use the same approach I use above to locate the item:
PropertyChanged consideration
If you did try this and you're not seeing any changes, it would be because the property on the model is not invoking PropertyChanged notification. Otherwise the binding in the template isn't informed of the value change and any converters (i.e. bool to color converter) wouldn't be triggered.
If you're using our Telerik.XamarinForms.ConversationalUI.TextMessage model as the base class, we already have INotifyPropertyChanged implemented. You can just call "OnPropertyChanged() in your property setters like I do above for ImageUrl and WebUrl.
Further Assistance
If you have trouble, please share your code and steps to reproduce with us in a Support Ticket. You have a priority support license and can use this link to open a new ticket - Get Support (choose UI for Xamarin).
Tip - If you do open a ticket and your repro project isn't able to connect to the messaging service, please add some sample messages in the chat service logic (instead of trying to connect). This way we'll be able to directly see the data and provide a much faster time to solution.
Regards,
Lance | Technical Support Engineer, Principal
Progress Telerik
You're correct, TemplateSelectors by design do not listen for property changes and the templates do not get switched after it's been rendered. If you wanted to trigger the TemplateSelector to render an item with a different template, you would remove and re-add the item.
// If a chat message's basic type is going to change and you need a new template from the selector,
// you can replace it in the collection
private
void
UpdateItem(BaseMessage newMessage)
{
// 1. Find the matching item's position in the list using some sort of ID property
var item = ChatMessages.FirstOrDefault(i => i.MessageId == newMessage.MessageId);
// 2. Get the original message's position
var index = ChatMessages.IndexOf(item);
// 3. Take out the original message
ChatMessages.Remove(item);
// 4. Insert the replacement message
ChatMessages.Insert(index, newMessage);
}
The resulting CollectionChanged event will trigger the TemplateSelector because a DataTemplate is needed for the item changes
Single DataTemplate Option
You've described the other good option with using a single item template. you can have a single DataTemplate that has UI elements for the different types of messages and show/hide/change parts of that template using properties of the message data model.
Using the same example from my earlier post, you can combine both ImageUrl and WebUrl in the same model and use a converter to show or hide the
public
class
MyMessage : TextMessage
{
private
string
_webUrl;
private
string
_imageUrl;
public
string
WebUrl
{
get
=> _webUrl;
set
{
if
(_webUrl != value)
{
_webUrl = value;
OnPropertyChanged();
}
}
}
public
string
ImageUrl
{
get
=> _imageUrl;
set
{
if
(_imageUrl != value)
{
_imageUrl = value;
OnPropertyChanged();
}
}
}
}
and the converter looks something like this:
public
class
EmptyStringToVisibilityConverter : IValueConverter
{
public
object
Convert(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
// If the string is not empty return true
if
(value
is
string
url)
{
if
(!
string
.IsNullOrEmpty(url))
{
return
true
;
}
}
return
false
;
}
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
throw
new
NotImplementedException();
}
}
You could use a single DataTemplate like this:
<
local:EmptyStringToVisibilityConverter
x:Key
=
"UrlIsVisibleConverter"
/>
<
DataTemplate
x:Key
=
"MyNormalMessageTemplate"
>
<
StackLayout
>
<
Label
Text
=
"{Binding Text}"
/>
<
Image
Source
=
"{Binding ImageUrl}"
IsVisible
=
"{Binding ImageUrl, Converter={StaticResource UrlIsVisibleConverter}}"
HeightRequest
=
"200"
/>
<
WebView
Source
=
"{Binding WebUrl}"
IsVisible
=
"{Binding ImageUrl, Converter={StaticResource UrlIsVisibleConverter}}"
HeightRequest
=
"400"
/>
</
StackLayout
>
</
DataTemplate
>
The same concept applies for anything else, like BackgroundColor, showing/hiding icons, etc. The only thing you really need to be sure of is that you're invoking property changed in some way when that message gets updated.
For example, if you're just trying to update the values of a message that already exists, you can use the same approach I use above to locate the item:
// This will check if the item exists and update the values
// If it does not exist, the message will be added to the chat
private
void
UpdateOrAddMessage(MyMessage incomingMessage)
{
// 1. Find the matching item's position in the list using some sort of ID property
var item = ChatMessages.FirstOrDefault(i => i.MessageId == incomingMessage.MessageId);
if
(item ==
null
)
{
// If the item is not present, just add it
ChatMessages.Add(incomingMessage);
}
else
{
// If the item does exist, update the existing instance's values to trigger PropertyChanged notifications
item.Text = incomingMessage.Text;
item.ImageUrl = incomingMessage.ImageUrl;
item.WebUrl = incomingMessage.WebUrl;
}
}
PropertyChanged consideration
If you did try this and you're not seeing any changes, it would be because the property on the model is not invoking PropertyChanged notification. Otherwise the binding in the template isn't informed of the value change and any converters (i.e. bool to color converter) wouldn't be triggered.
If you're using our Telerik.XamarinForms.ConversationalUI.TextMessage model as the base class, we already have INotifyPropertyChanged implemented. You can just call "OnPropertyChanged() in your property setters like I do above for ImageUrl and WebUrl.
Further Assistance
If you have trouble, please share your code and steps to reproduce with us in a Support Ticket. You have a priority support license and can use this link to open a new ticket - Get Support (choose UI for Xamarin).
Tip - If you do open a ticket and your repro project isn't able to connect to the messaging service, please add some sample messages in the chat service logic (instead of trying to connect). This way we'll be able to directly see the data and provide a much faster time to solution.
Regards,
Lance | Technical Support Engineer, Principal
Progress Telerik
Do you want to have your say when we set our development plans?
Do you want to know when a feature you care about is added or when a bug fixed?
Explore the
Telerik Feedback Portal
and vote to affect the priority of the items