ChatView | Images & Attachments
ChatView has a built-in image attachment affordance gated by the CanSendImages permission, and taps on image bubbles open the built-in ImageViewer.
Sending Images
Section titled “Sending Images”When CanSendImages is set (see Permissions), the control shows an attach affordance offering Gallery, plus Camera when the platform/device supports capture:
- MAUI — uses .NET MAUI Essentials
MediaPicker. Camera is shown only whenMediaPicker.Default.IsCaptureSupportedistrue(most desktop targets show Gallery only).PickPhotoAsync()/CapturePhotoAsync()produce the stream. - Blazor — uses
<InputFile accept="image/*">; thecaptureattribute hints the mobile browser camera. Desktop browsers show file/gallery selection only.
CanSendImages is independent of CanSendMessages, so a session can be image-only, text-only, or both. A v1 message is either text or an image, not both.
OutgoingAttachment
Section titled “OutgoingAttachment”The control packages the chosen image into an OutgoingMessage with an OutgoingAttachment and calls SendMessageAsync:
public record OutgoingMessage(string? Body, OutgoingAttachment? Attachment = null, string ClientMessageId = "");
public record OutgoingAttachment( ChatAttachmentKind Kind, // Image (Video/Audio/File reserved) Stream Content, // the provider OWNS and DISPOSES this stream after upload string FileName, string ContentType);
public enum ChatAttachmentKind { Image }:::caution The provider owns the stream
The control supplies Attachment.Content; your provider must dispose it once it has uploaded (or copied) the bytes. The control will not dispose it for you.
:::
public Task<ChatMessage> SendMessageAsync(OutgoingMessage message, CancellationToken ct = default){ string? imageUrl = null; if (message.Attachment is not null) { // upload the bytes to your storage, then dispose the stream you were handed imageUrl = MyUploadImage(message.Attachment.Content, message.Attachment.FileName, message.Attachment.ContentType); message.Attachment.Content.Dispose(); }
var stored = new ChatMessage( MessageId: this.store.NextMessageId(), ClientMessageId: string.IsNullOrEmpty(message.ClientMessageId) ? null : message.ClientMessageId, SenderId: this.CurrentUserId, Body: message.Body, ImageUrl: imageUrl, Status: MessageStatus.Sent, StatusReason: null, Timestamp: DateTimeOffset.Now, EditedTimestamp: null, Reactions: Array.Empty<Reaction>(), ReadReceipts: Array.Empty<ReadReceipt>() ); lock (this.store.Sync) this.store.Messages.Add(stored); return Task.FromResult(stored);}The optimistic image flow
Section titled “The optimistic image flow”- The user picks/captures an image → the control builds the
OutgoingAttachmentand aClientMessageId. - A
Sendingbubble shows a local preview immediately. - The provider uploads and returns the stored
ChatMessage(sameClientMessageId) withImageUrlpopulated. - The control reconciles the optimistic bubble; subsequent taps use the remote
ImageUrl.
Rejecting oversized/too-many images
Section titled “Rejecting oversized/too-many images”The control never pre-checks image size or count — enforce limits in the provider and throw ChatSendRejectedException, which renders the bubble as Rejected (no retry):
if (message.Attachment is not null && tooLarge) throw new ChatSendRejectedException("Image exceeds 10 MB.", SendRejectionKind.AttachmentTooLarge);See Messages & Paging.
Tapping an Image → ImageViewer
Section titled “Tapping an Image → ImageViewer”By default, tapping an image bubble opens the built-in ImageViewer (pinch / pan / double-tap zoom). This is a client-side behavior — not a provider call.
<shiny:ChatView Provider="{Binding Provider}" SessionId="{Binding SessionId}" OpenImagesInViewer="True" />| Property | Type | Default | Description |
|---|---|---|---|
OpenImagesInViewer | bool | true | Tapping an image bubble opens the built-in ImageViewer. Set false to handle the tap yourself (e.g. with a custom MessageTemplate). |
For an optimistic send, the viewer opens on the local preview; once the echo populates the remote ImageUrl, subsequent taps use it. Non-image bubble taps remain a notification only (MAUI exposes the MessageTapped event).
Next Steps
Section titled “Next Steps”- Permissions —
CanSendImages - Messages & Paging — optimistic send, failed vs rejected
- ImageViewer — the zoom/pan viewer ChatView reuses