diff --git a/samples/ControlGallery/Views/Collections/CarouselViews.razor b/samples/ControlGallery/Views/Collections/CarouselViews.razor
new file mode 100644
index 00000000..254c3a2b
--- /dev/null
+++ b/samples/ControlGallery/Views/Collections/CarouselViews.razor
@@ -0,0 +1,47 @@
+@page "/carouselviewplayground"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ Item[] items = new Item[] {
+ new("Nemo", "https://static.wikia.nocookie.net/pixar/images/a/aa/Nemo-FN.png/revision/latest/smart/width/250/height/250?cb=20160710221104"),
+ new("Dory", "https://static.wikia.nocookie.net/pixar/images/1/1f/Dory-white.jpg/revision/latest/scale-to-width-down/350?cb=20110924203518"),
+ new("Bruce", "https://static.wikia.nocookie.net/pixar/images/e/e3/Bruce-render.png/revision/latest/scale-to-width-down/350?cb=20181210152244"),
+ new("Gill", "https://static.wikia.nocookie.net/pixar/images/7/72/Gill.png/revision/latest/scale-to-width-down/350?cb=20210322233843")
+ };
+
+ Item currentItem;
+ int currentPosition;
+
+ record Item(string Name, ImageSource Image);
+}
diff --git a/samples/ControlGallery/Views/PlaygroundList.razor b/samples/ControlGallery/Views/PlaygroundList.razor
index d1a63c90..963041df 100644
--- a/samples/ControlGallery/Views/PlaygroundList.razor
+++ b/samples/ControlGallery/Views/PlaygroundList.razor
@@ -17,6 +17,7 @@
+
diff --git a/src/BlazorBindings.Maui/Elements/CarouselView.cs b/src/BlazorBindings.Maui/Elements/CarouselView.cs
new file mode 100644
index 00000000..001cad4f
--- /dev/null
+++ b/src/BlazorBindings.Maui/Elements/CarouselView.cs
@@ -0,0 +1,96 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using BlazorBindings.Core;
+using BlazorBindings.Maui.Elements.Handlers;
+using Microsoft.AspNetCore.Components;
+using Microsoft.Maui;
+using MC = Microsoft.Maui.Controls;
+
+namespace BlazorBindings.Maui.Elements
+{
+ public class CarouselView : ItemsView
+ {
+ private T currentItem;
+ private bool _currentItemSet;
+
+ static CarouselView()
+ {
+ ElementHandlerRegistry.RegisterElementHandler>(
+ renderer => new CarouselViewHandler(renderer, new MC.CarouselView()));
+ }
+
+ [Parameter] public bool? IsBounceEnabled { get; set; }
+ [Parameter] public bool? IsScrollAnimated { get; set; }
+ [Parameter] public bool? IsSwipeEnabled { get; set; }
+ [Parameter] public MC.LinearItemsLayout ItemsLayout { get; set; }
+ [Parameter] public bool? Loop { get; set; }
+ [Parameter] public Thickness? PeekAreaInsets { get; set; }
+ [Parameter] public int? Position { get; set; }
+ [Parameter] public EventCallback PositionChanged { get; set; }
+ [Parameter] public EventCallback CurrentItemChanged { get; set; }
+
+ [Parameter]
+ public T CurrentItem
+ {
+ get => currentItem;
+ set
+ {
+ // When T is struct type (e.g. int), we need to be able to understand
+ // if the property was set to a default value (e.g. 0) or it was not set at all.
+ _currentItemSet = true;
+ currentItem = value;
+ }
+ }
+
+ public new MC.CarouselView NativeControl => (ElementHandler as CarouselViewHandler)?.CarouselViewControl;
+
+ protected override void RenderAttributes(AttributesBuilder builder)
+ {
+ base.RenderAttributes(builder);
+
+ if (IsBounceEnabled != null)
+ {
+ builder.AddAttribute(nameof(IsBounceEnabled), IsBounceEnabled.Value);
+ }
+ if (IsScrollAnimated != null)
+ {
+ builder.AddAttribute(nameof(IsScrollAnimated), IsScrollAnimated.Value);
+ }
+ if (IsSwipeEnabled != null)
+ {
+ builder.AddAttribute(nameof(IsSwipeEnabled), IsSwipeEnabled.Value);
+ }
+ if (ItemsLayout != null)
+ {
+ builder.AddAttribute(nameof(ItemsLayout), AttributeHelper.ObjectToDelegate(ItemsLayout));
+ }
+ if (Loop != null)
+ {
+ builder.AddAttribute(nameof(Loop), Loop.Value);
+ }
+ if (PeekAreaInsets != null)
+ {
+ builder.AddAttribute(nameof(PeekAreaInsets), AttributeHelper.ThicknessToString(PeekAreaInsets.Value));
+ }
+ if (Position != null)
+ {
+ builder.AddAttribute(nameof(Position), Position.Value);
+ }
+ if (_currentItemSet && CurrentItem != null)
+ {
+ builder.AddAttribute(nameof(CurrentItem), AttributeHelper.ObjectToDelegate(CurrentItem)); ;
+ }
+ if (PositionChanged.HasDelegate)
+ {
+ builder.AddAttribute("onPositionChanged", EventCallback.Factory.Create(this,
+ args => PositionChanged.InvokeAsync(args.CurrentPosition)));
+ }
+ if (CurrentItemChanged.HasDelegate)
+ {
+ builder.AddAttribute("onCurrentItemChanged", EventCallback.Factory.Create(this,
+ args => CurrentItemChanged.InvokeAsync((T)args.CurrentItem)));
+ }
+ }
+ }
+}
diff --git a/src/BlazorBindings.Maui/Elements/Handlers/CarouselViewHandler.cs b/src/BlazorBindings.Maui/Elements/Handlers/CarouselViewHandler.cs
new file mode 100644
index 00000000..6d1b91bf
--- /dev/null
+++ b/src/BlazorBindings.Maui/Elements/Handlers/CarouselViewHandler.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using BlazorBindings.Core;
+using Microsoft.Maui;
+using System;
+using MC = Microsoft.Maui.Controls;
+
+namespace BlazorBindings.Maui.Elements.Handlers
+{
+ public class CarouselViewHandler : ItemsViewHandler
+ {
+ private static readonly bool IsBounceEnabledDefaultValue = MC.CarouselView.IsBounceEnabledProperty.DefaultValue is bool value ? value : default;
+ private static readonly bool IsScrollAnimatedDefaultValue = MC.CarouselView.IsScrollAnimatedProperty.DefaultValue is bool value ? value : default;
+ private static readonly bool IsSwipeEnabledDefaultValue = MC.CarouselView.IsSwipeEnabledProperty.DefaultValue is bool value ? value : default;
+ private static readonly MC.LinearItemsLayout ItemsLayoutDefaultValue = MC.CarouselView.ItemsLayoutProperty.DefaultValue is MC.LinearItemsLayout value ? value : default;
+ private static readonly bool LoopDefaultValue = MC.CarouselView.LoopProperty.DefaultValue is bool value ? value : default;
+ private static readonly Thickness PeekAreaInsetsDefaultValue = MC.CarouselView.PeekAreaInsetsProperty.DefaultValue is Thickness value ? value : default;
+ private static readonly int PositionDefaultValue = MC.CarouselView.PositionProperty.DefaultValue is int value ? value : default;
+
+ public CarouselViewHandler(NativeComponentRenderer renderer, MC.CarouselView carouselViewControl) : base(renderer, carouselViewControl)
+ {
+ CarouselViewControl = carouselViewControl ?? throw new ArgumentNullException(nameof(carouselViewControl));
+
+ Initialize(renderer);
+ }
+
+ public MC.CarouselView CarouselViewControl { get; }
+
+ public override void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
+ {
+ switch (attributeName)
+ {
+ case nameof(MC.CarouselView.IsBounceEnabled):
+ CarouselViewControl.IsBounceEnabled = AttributeHelper.GetBool(attributeValue, IsBounceEnabledDefaultValue);
+ break;
+ case nameof(MC.CarouselView.IsScrollAnimated):
+ CarouselViewControl.IsScrollAnimated = AttributeHelper.GetBool(attributeValue, IsScrollAnimatedDefaultValue);
+ break;
+ case nameof(MC.CarouselView.IsSwipeEnabled):
+ CarouselViewControl.IsSwipeEnabled = AttributeHelper.GetBool(attributeValue, IsSwipeEnabledDefaultValue);
+ break;
+ case nameof(MC.CarouselView.ItemsLayout):
+ CarouselViewControl.ItemsLayout = AttributeHelper.DelegateToObject(attributeValue, ItemsLayoutDefaultValue);
+ break;
+ case nameof(MC.CarouselView.Loop):
+ CarouselViewControl.Loop = AttributeHelper.GetBool(attributeValue, LoopDefaultValue);
+ break;
+ case nameof(MC.CarouselView.PeekAreaInsets):
+ CarouselViewControl.PeekAreaInsets = AttributeHelper.StringToThickness(attributeValue, PeekAreaInsetsDefaultValue);
+ break;
+ case nameof(MC.CarouselView.Position):
+ CarouselViewControl.Position = AttributeHelper.GetInt(attributeValue, PositionDefaultValue);
+ break;
+ case nameof(MC.CarouselView.CurrentItem):
+ CarouselViewControl.CurrentItem = AttributeHelper.DelegateToObject