Skip to content
Introducing AI Conversations: Natural Language Interaction for Your Apps! Learn More

ImageViewer

A full-screen image overlay with pinch-to-zoom, pan when zoomed, double-tap to toggle zoom, animated open/close transitions, and a close button.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor
GalleryViewer
GalleryViewer
<Grid>
<!-- Page content with tappable images -->
<ScrollView>
<VerticalStackLayout>
<Image Source="photo.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenViewerCommand}"
CommandParameter="photo.png" />
</Image.GestureRecognizers>
</Image>
</VerticalStackLayout>
</ScrollView>
<!-- ImageViewer overlays on top -->
<shiny:ImageViewer Source="{Binding SelectedImage}"
IsOpen="{Binding IsViewerOpen}" />
</Grid>
PropertyTypeDefaultDescription
SourceImageSource?nullThe image to display
IsOpenboolfalseShow/hide the viewer (TwoWay bindable)
AspectAspectAspectFitImage aspect ratio mode (MAUI only)
MaxZoomdouble5.0Maximum pinch zoom scale
CloseButtonTemplateDataTemplate?nullCustom close button template (tapping closes the viewer)
HeaderTemplateDataTemplate?nullCustom header overlay at the top of the viewer
FooterTemplateDataTemplate?nullCustom footer overlay at the bottom of the viewer
UseFeedbackbooltrueHaptic click on double-tap zoom
  • Pinch-to-zoom — Two-finger pinch scales around the pinch origin, clamped between 1x and MaxZoom
  • Pan when zoomed — One-finger pan enabled after zooming in, translation clamped to image bounds
  • Double-tap to zoom — Double-tap zooms to 2.5x centered on the tap point; double-tap again resets
  • Animated open/close — Backdrop, image, and close button fade together (250ms)
  • Close button — ”✕” button in the top-right corner (customizable via CloseButtonTemplate)
  • Header/Footer templates — Optional overlays at the top/bottom for custom UI (e.g. image info, action buttons)
  • Backdrop — Black overlay that swallows touches so nothing passes through to the page behind
public partial class ImageViewerViewModel : ObservableObject
{
[ObservableProperty] ImageSource? selectedImage;
[ObservableProperty] bool isViewerOpen;
[RelayCommand]
void OpenViewer(string imageSource)
{
SelectedImage = imageSource;
IsViewerOpen = true;
}
}
@using Shiny.Blazor.Controls
<div style="position: relative;">
<!-- Tappable image -->
<img src="photo.jpg" @onclick="() => OpenViewer("photo.jpg")"
style="cursor: pointer;" />
<!-- ImageViewer overlay -->
<ImageViewer Source="@selectedImage"
IsOpen="@isViewerOpen"
IsOpenChanged="v => isViewerOpen = v"
MaxZoom="5.0" />
</div>
@code {
string? selectedImage;
bool isViewerOpen;
void OpenViewer(string imageUrl)
{
selectedImage = imageUrl;
isViewerOpen = true;
}
}
claude plugin marketplace add shinyorg/skills
claude plugin install shiny-controls@shiny
copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny-controls@shiny
View shiny-controls Plugin