The CarouselView in .NET MAUI is a powerful control for building swipeable collections, perfect for galleries, tutorials, or sliders. But what if you want zoomable content—like images—inside each Carousel item?
This post walks you through combining CarouselView with a custom PinchToZoomContainer, enabling pinch-to-zoom and pan interactions inside carousel slides without breaking swipe navigation.
The Challenge
When you place a zoomable view inside a CarouselView, gesture conflicts arise:
- Swiping to the next item vs. panning the zoomed content
- Double-tap or pinch interfering with CarouselView's native swipe
If not handled carefully, users will either fail to zoom, or accidentally change slides while zooming. Our goal: smooth gestures without compromise.
The Building Block: PinchToZoomContainer
We use a custom ContentView (PinchToZoomContainer) that:
- Handles pinch-to-zoom and double-tap
- Allows panning only when zoomed in
- Resets to original scale when zoomed out
Here's a quick recap of its structure:
public class PinchToZoomContainer : ContentView
{
// Handles pinch, pan, and double-tap gestures
}
It wraps your content (e.g., an image) and manages gestures smartly.
Integrating with CarouselView
Now, embed the PinchToZoomContainer in each carousel item:
<CarouselView ItemsSource="{Binding Images}">
<CarouselView.ItemTemplate>
<DataTemplate>
<controls:PinchToZoomContainer>
<Image Source="{Binding}" Aspect="AspectFit" />
</controls:PinchToZoomContainer>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
🔥 Key Idea: The zoom container manages gestures at the item level, allowing CarouselView to handle swipes only when the content is not zoomed in.
Gesture Conflict Handling
The magic happens in your zoom container’s pan gesture logic:
if (Content.Scale <= 1)
{
return; // Let CarouselView handle the swipe
}
This line ensures that:
- Zoomed out: CarouselView handles swipe
- Zoomed in: Only the content pans, swipe is blocked
Also, we dynamically add or remove pan gestures based on zoom state:
if (_currentScale > 1 && !GestureRecognizers.Contains(_panGesture))
{
GestureRecognizers.Add(_panGesture);
}
This keeps interactions natural and intuitive.
Full Example
Here’s a complete snippet
<CarouselView ItemsSource="{Binding ImageSources}">
<CarouselView.ItemTemplate>
<DataTemplate>
<controls:PinchToZoomContainer>
<Image Source="{Binding}" Aspect="AspectFit" />
</controls:PinchToZoomContainer>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
In the ViewModel:
public ObservableCollection<string> ImageSources { get; } = new()
{
"image1.jpg",
"image2.jpg",
"image3.jpg"
};
What Works Well
- ✅ Pinch-to-zoom works seamlessly inside each slide
- ✅ Swipe between slides only when not zoomed in
- ✅ Double-tap toggles zoom
- ✅ Panning stays within bounds
Wrap Up
You don’t have to choose between pinch-to-zoom and carousel swiping—you can have both! By isolating gestures within a PinchToZoomContainer and being smart about when panning is allowed, your users can enjoy a smooth, professional zoom gallery experience in .NET MAUI.
Check out the GitHub repo: PinchToZoomCarouselApp
Great news! free tokens now live for Dev.to contributors to celebrate our authors' impact in Web3! Visit the claim page here (wallet connection required). – Dev.to Community Support