ChatView | Message Templates
Overview
Section titled “Overview”Message templates let you replace the default text/image rendering inside a chat bubble with completely custom content. The bubble chrome (alignment, colors, corner radius, avatar, name, timestamp, acknowledgements, tool button) is still managed by ChatView — your template controls only the inner content area.
This is a MAUI-only feature.
When to Use Templates
Section titled “When to Use Templates”Use templates when you need messages that go beyond plain text or images:
- Action buttons (“Accept”, “Decline”)
- Product cards with images and details
- Location previews with maps
- File attachments with metadata
- Poll/survey messages with checkboxes
- Code snippets with syntax highlighting
- Payment/invoice cards
- Interactive forms
MessageTemplate (Single Template)
Section titled “MessageTemplate (Single Template)”Apply one template to all messages:
<shiny:ChatView Messages="{Binding Messages}" SendCommand="{Binding SendCommand}"> <shiny:ChatView.MessageTemplate> <DataTemplate x:DataType="shiny:ChatMessage"> <VerticalStackLayout Spacing="4"> <Label Text="{Binding Text}" FontSize="15" LineBreakMode="WordWrap" /> <Label Text="{Binding Timestamp, StringFormat='{0:HH:mm}'}" FontSize="10" TextColor="Gray" HorizontalOptions="End" /> </VerticalStackLayout> </DataTemplate> </shiny:ChatView.MessageTemplate></shiny:ChatView>This replaces the default rendering for every message. The bubble padding is (12, 8) when a template is active.
MessageTemplateSelector (Per-Message-Type Templates)
Section titled “MessageTemplateSelector (Per-Message-Type Templates)”For different visual treatments per message type, use a DataTemplateSelector. This is the most powerful approach.
Step 1: Create Message Subclasses
Section titled “Step 1: Create Message Subclasses”Extend ChatMessage with properties specific to each message type:
public class ActionChatMessage : ChatMessage{ public string ActionText { get; set; } = "Accept"; public string DismissText { get; set; } = "Dismiss";}
public class CardChatMessage : ChatMessage{ public string CardTitle { get; set; } = string.Empty; public string CardImageUrl { get; set; } = string.Empty;}
public class LocationChatMessage : ChatMessage{ public double Latitude { get; set; } public double Longitude { get; set; } public string? Address { get; set; }}
public class FileChatMessage : ChatMessage{ public string FileName { get; set; } = string.Empty; public string FileSize { get; set; } = string.Empty; public string FileIcon { get; set; } = "📄";}Step 2: Create a Template Selector
Section titled “Step 2: Create a Template Selector”Route each message type to the appropriate template:
public class ChatMessageTemplateSelector : DataTemplateSelector{ public DataTemplate? TextTemplate { get; set; } public DataTemplate? ActionTemplate { get; set; } public DataTemplate? CardTemplate { get; set; } public DataTemplate? LocationTemplate { get; set; } public DataTemplate? FileTemplate { get; set; }
protected override DataTemplate? OnSelectTemplate(object item, BindableObject container) { return item switch { LocationChatMessage => LocationTemplate, ActionChatMessage => ActionTemplate, CardChatMessage => CardTemplate, FileChatMessage => FileTemplate, _ => TextTemplate }; }}Step 3: Define Templates in XAML
Section titled “Step 3: Define Templates in XAML”<shiny:ChatView Messages="{Binding Messages}" Participants="{Binding Participants}" IsMultiPerson="True" SendCommand="{Binding SendCommand}"> <shiny:ChatView.MessageTemplateSelector> <local:ChatMessageTemplateSelector>
<!-- Default: plain text --> <local:ChatMessageTemplateSelector.TextTemplate> <DataTemplate x:DataType="shiny:ChatMessage"> <Label Text="{Binding Text}" FontSize="15" LineBreakMode="WordWrap" /> </DataTemplate> </local:ChatMessageTemplateSelector.TextTemplate>
<!-- Action message: text + buttons --> <local:ChatMessageTemplateSelector.ActionTemplate> <DataTemplate x:DataType="local:ActionChatMessage"> <VerticalStackLayout Spacing="8"> <Label Text="{Binding Text}" FontSize="15" LineBreakMode="WordWrap" /> <HorizontalStackLayout Spacing="8"> <Button Text="{Binding ActionText}" FontSize="12" HeightRequest="32" Padding="12,0" BackgroundColor="#007AFF" TextColor="White" CornerRadius="16" Command="{Binding Source={RelativeSource AncestorType={x:Type local:ChatViewModel}}, Path=AcceptCommand}" CommandParameter="{Binding .}" /> <Button Text="{Binding DismissText}" FontSize="12" HeightRequest="32" Padding="12,0" BackgroundColor="#E5E5EA" TextColor="#333" CornerRadius="16" Command="{Binding Source={RelativeSource AncestorType={x:Type local:ChatViewModel}}, Path=DismissCommand}" CommandParameter="{Binding .}" /> </HorizontalStackLayout> </VerticalStackLayout> </DataTemplate> </local:ChatMessageTemplateSelector.ActionTemplate>
<!-- Card message: image + title + description --> <local:ChatMessageTemplateSelector.CardTemplate> <DataTemplate x:DataType="local:CardChatMessage"> <Border StrokeThickness="1" Stroke="#E0E0E0" StrokeShape="RoundRectangle 8" Padding="0" BackgroundColor="White"> <VerticalStackLayout> <Image Source="{Binding CardImageUrl}" HeightRequest="120" Aspect="AspectFill" /> <VerticalStackLayout Padding="12" Spacing="4"> <Label Text="{Binding CardTitle}" FontSize="14" FontAttributes="Bold" /> <Label Text="{Binding Text}" FontSize="12" TextColor="Gray" LineBreakMode="WordWrap" /> </VerticalStackLayout> </VerticalStackLayout> </Border> </DataTemplate> </local:ChatMessageTemplateSelector.CardTemplate>
<!-- File attachment --> <local:ChatMessageTemplateSelector.FileTemplate> <DataTemplate x:DataType="local:FileChatMessage"> <HorizontalStackLayout Spacing="8"> <Label Text="{Binding FileIcon}" FontSize="24" VerticalOptions="Center" /> <VerticalStackLayout VerticalOptions="Center"> <Label Text="{Binding FileName}" FontSize="14" FontAttributes="Bold" /> <Label Text="{Binding FileSize}" FontSize="11" TextColor="Gray" /> </VerticalStackLayout> </HorizontalStackLayout> </DataTemplate> </local:ChatMessageTemplateSelector.FileTemplate>
</local:ChatMessageTemplateSelector> </shiny:ChatView.MessageTemplateSelector></shiny:ChatView>Step 4: Add Messages with Subclasses
Section titled “Step 4: Add Messages with Subclasses”// Regular text message — uses TextTemplateMessages.Add(new ChatMessage{ Text = "Here's what I found:", SenderId = "bot", IsFromMe = false});
// Action message — uses ActionTemplateMessages.Add(new ActionChatMessage{ Text = "Would you like to schedule a follow-up meeting?", ActionText = "Schedule", DismissText = "No thanks", SenderId = "bot", IsFromMe = false});
// Card message — uses CardTemplateMessages.Add(new CardChatMessage{ Text = "Premium wireless headphones with noise cancellation", CardTitle = "Sony WH-1000XM5", CardImageUrl = "https://example.com/headphones.jpg", SenderId = "bot", IsFromMe = false});
// File message — uses FileTemplateMessages.Add(new FileChatMessage{ Text = "Here's the report", FileName = "Q4-Report.pdf", FileSize = "2.4 MB", FileIcon = "📊", SenderId = "bot", IsFromMe = false});Binding to ViewModel Commands
Section titled “Binding to ViewModel Commands”Templates render inside the ChatView’s CollectionView. To reach your ViewModel’s commands, use RelativeSource:
<Button Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyViewModel}}, Path=MyCommand}" CommandParameter="{Binding .}" />{Binding .}passes the currentChatMessage(or subclass) as the command parameter- Replace
local:MyViewModelwith your ViewModel type
What Templates Control vs. What ChatView Controls
Section titled “What Templates Control vs. What ChatView Controls”| ChatView handles | Your template handles |
|---|---|
| Bubble alignment (left/right) | Content layout inside bubble |
| Bubble background color | Text styling, fonts, colors |
| Bubble corner radius & tail | Images, buttons, cards, forms |
| Avatar and display name | Interactive elements |
| Timestamp display | Custom metadata rendering |
| Acknowledgement badges | — |
| Tool button (⋯) | — |
| Unsent opacity (DateSent) | — |
| Message grouping & spacing | — |
Important Notes
Section titled “Important Notes”Use Cases Summary
Section titled “Use Cases Summary”| Scenario | Approach |
|---|---|
| AI with action buttons | ActionChatMessage + button template |
| E-commerce product cards | CardChatMessage + image + details |
| Location sharing | LocationChatMessage + map view |
| File attachments | FileChatMessage + icon + metadata |
| Polls/surveys | Custom subclass + radio buttons |
| Code snippets | Custom subclass + monospace + syntax colors |
| Payment requests | Custom subclass + amount + pay button |
| Calendar invites | Custom subclass + date/time + RSVP buttons |