Browse Source

Added more Carousel samples (#20932)

* Added all the Carousel samples

* Fixes

* Updated sample

* More changes
pull/20940/head
Javier Suárez 5 days ago
committed by GitHub
parent
commit
7512fa4bd0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      samples/ControlCatalog/MainView.xaml
  2. 11
      samples/ControlCatalog/Pages/CarouselDemoPage.xaml
  3. 53
      samples/ControlCatalog/Pages/CarouselDemoPage.xaml.cs
  4. 119
      samples/ControlCatalog/Pages/CarouselPage/CarouselCustomizationPage.xaml
  5. 48
      samples/ControlCatalog/Pages/CarouselPage/CarouselCustomizationPage.xaml.cs
  6. 60
      samples/ControlCatalog/Pages/CarouselPage/CarouselDataBindingPage.xaml
  7. 95
      samples/ControlCatalog/Pages/CarouselPage/CarouselDataBindingPage.xaml.cs
  8. 557
      samples/ControlCatalog/Pages/CarouselPage/CarouselGalleryAppPage.xaml
  9. 101
      samples/ControlCatalog/Pages/CarouselPage/CarouselGalleryAppPage.xaml.cs
  10. 93
      samples/ControlCatalog/Pages/CarouselPage/CarouselGesturesPage.xaml
  11. 59
      samples/ControlCatalog/Pages/CarouselPage/CarouselGesturesPage.xaml.cs
  12. 74
      samples/ControlCatalog/Pages/CarouselPage/CarouselGettingStartedPage.xaml
  13. 40
      samples/ControlCatalog/Pages/CarouselPage/CarouselGettingStartedPage.xaml.cs
  14. 140
      samples/ControlCatalog/Pages/CarouselPage/CarouselMultiItemPage.xaml
  15. 47
      samples/ControlCatalog/Pages/CarouselPage/CarouselMultiItemPage.xaml.cs
  16. 97
      samples/ControlCatalog/Pages/CarouselPage/CarouselTransitionsPage.xaml
  17. 66
      samples/ControlCatalog/Pages/CarouselPage/CarouselTransitionsPage.xaml.cs
  18. 132
      samples/ControlCatalog/Pages/CarouselPage/CarouselVerticalPage.xaml
  19. 39
      samples/ControlCatalog/Pages/CarouselPage/CarouselVerticalPage.xaml.cs
  20. 14
      src/Avalonia.Controls/VirtualizingCarouselPanel.cs

6
samples/ControlCatalog/MainView.xaml

@ -54,8 +54,10 @@
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CommandBarPage />
</TabItem>
<TabItem Header="Carousel">
<pages:CarouselPage />
<TabItem Header="Carousel"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CarouselDemoPage />
</TabItem>
<TabItem Header="CheckBox">

11
samples/ControlCatalog/Pages/CarouselDemoPage.xaml

@ -0,0 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselDemoPage">
<NavigationPage x:Name="SampleNav">
<NavigationPage.Styles>
<Style Selector="NavigationPage#SampleNav /template/ Border#PART_NavigationBar">
<Setter Property="Background" Value="Transparent" />
</Style>
</NavigationPage.Styles>
</NavigationPage>
</UserControl>

53
samples/ControlCatalog/Pages/CarouselDemoPage.xaml.cs

@ -0,0 +1,53 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselDemoPage : UserControl
{
private static readonly (string Group, string Title, string Description, Func<UserControl> Factory)[] Demos =
{
// Overview
("Overview", "Getting Started",
"Basic Carousel with image items and previous/next navigation buttons.",
() => new CarouselGettingStartedPage()),
// Features
("Features", "Transitions",
"Configure page transitions: PageSlide, CrossFade, 3D Rotation, or None.",
() => new CarouselTransitionsPage()),
("Features", "Customization",
"Adjust orientation and transition type to tailor the carousel layout.",
() => new CarouselCustomizationPage()),
("Features", "Gestures & Keyboard",
"Navigate items via swipe gesture and arrow keys. Toggle each input mode on and off.",
() => new CarouselGesturesPage()),
("Features", "Vertical Orientation",
"Carousel with Orientation set to Vertical, navigated with Up/Down keys, swipe, or buttons.",
() => new CarouselVerticalPage()),
("Features", "Multi-Item Peek",
"Adjust ViewportFraction to show multiple items simultaneously with adjacent cards peeking.",
() => new CarouselMultiItemPage()),
("Features", "Data Binding",
"Bind Carousel to an ObservableCollection and add, remove, or shuffle items at runtime.",
() => new CarouselDataBindingPage()),
// Showcases
("Showcases", "Curated Gallery",
"Editorial art gallery app with DrawerPage navigation, hero Carousel with PipsPager dots, and a horizontal peek carousel for collection highlights.",
() => new CarouselGalleryAppPage()),
};
public CarouselDemoPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null);
}
}
}

119
samples/ControlCatalog/Pages/CarouselPage/CarouselCustomizationPage.xaml

@ -0,0 +1,119 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselCustomizationPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontWeight="SemiBold" FontSize="13" />
<Button x:Name="PreviousButton"
Content="Previous"
HorizontalAlignment="Stretch" />
<Button x:Name="NextButton"
Content="Next"
HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Orientation" FontWeight="SemiBold" FontSize="13" />
<ComboBox x:Name="OrientationCombo"
HorizontalAlignment="Stretch"
SelectedIndex="0">
<ComboBoxItem>Horizontal</ComboBoxItem>
<ComboBoxItem>Vertical</ComboBoxItem>
</ComboBox>
<TextBlock Text="Viewport Fraction" FontWeight="SemiBold" FontSize="13" />
<Grid ColumnDefinitions="*,48" ColumnSpacing="8">
<Slider x:Name="ViewportSlider"
Minimum="0.33"
Maximum="1.0"
Value="1.0"
TickFrequency="0.01"
HorizontalAlignment="Stretch" />
<TextBlock x:Name="ViewportLabel"
Grid.Column="1"
Text="1.00"
VerticalAlignment="Center"
HorizontalAlignment="Right"
FontWeight="SemiBold" />
</Grid>
<TextBlock x:Name="ViewportHint"
Text="1.00 shows a single full page."
FontSize="11"
Opacity="0.6"
TextWrapping="Wrap" />
<Separator />
<TextBlock Text="Options" FontWeight="SemiBold" FontSize="14" />
<CheckBox x:Name="WrapSelectionCheck"
Content="Wrap Selection"
IsChecked="False"
IsCheckedChanged="OnWrapSelectionChanged" />
<CheckBox x:Name="SwipeEnabledCheck"
Content="Swipe Enabled"
IsChecked="False"
IsCheckedChanged="OnSwipeEnabledChanged" />
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText"
Text="Orientation: Horizontal"
Opacity="0.7"
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<Carousel x:Name="DemoCarousel" Height="300">
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Horizontal" />
</Carousel.PageTransition>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/delicate-arch-896885_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 1: Delicate Arch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/hirsch-899118_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 2: Hirsch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/maple-leaf-888807_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 3: Maple Leaf" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
</Carousel>
</Border>
</DockPanel>
</UserControl>

48
samples/ControlCatalog/Pages/CarouselPage/CarouselCustomizationPage.xaml.cs

@ -0,0 +1,48 @@
using System;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselCustomizationPage : UserControl
{
public CarouselCustomizationPage()
{
InitializeComponent();
PreviousButton.Click += (_, _) => DemoCarousel.Previous();
NextButton.Click += (_, _) => DemoCarousel.Next();
OrientationCombo.SelectionChanged += (_, _) => ApplyOrientation();
ViewportSlider.ValueChanged += OnViewportFractionChanged;
}
private void ApplyOrientation()
{
var horizontal = OrientationCombo.SelectedIndex == 0;
var axis = horizontal ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical;
DemoCarousel.PageTransition = new PageSlide(TimeSpan.FromSeconds(0.25), axis);
StatusText.Text = $"Orientation: {(horizontal ? "Horizontal" : "Vertical")}";
}
private void OnViewportFractionChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
var value = Math.Round(e.NewValue, 2);
DemoCarousel.ViewportFraction = value;
ViewportLabel.Text = value.ToString("0.00");
ViewportHint.Text = value >= 1d ?
"1.00 shows a single full page." :
$"{1d / value:0.##} pages fit in view. Try 0.80 for peeking.";
}
private void OnWrapSelectionChanged(object? sender, RoutedEventArgs e)
{
DemoCarousel.WrapSelection = WrapSelectionCheck.IsChecked == true;
}
private void OnSwipeEnabledChanged(object? sender, RoutedEventArgs e)
{
DemoCarousel.IsSwipeEnabled = SwipeEnabledCheck.IsChecked == true;
}
}
}

60
samples/ControlCatalog/Pages/CarouselPage/CarouselDataBindingPage.xaml

@ -0,0 +1,60 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
x:Class="ControlCatalog.Pages.CarouselDataBindingPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Collection" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontWeight="SemiBold" FontSize="13" />
<Button x:Name="PreviousButton" Content="Previous" HorizontalAlignment="Stretch" />
<Button x:Name="NextButton" Content="Next" HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Modify Items" FontWeight="SemiBold" FontSize="13" />
<TextBlock TextWrapping="Wrap" FontSize="11" Opacity="0.6"
Text="The Carousel is bound to an ObservableCollection. Changes reflect immediately." />
<Button x:Name="AddButton" Content="Add Item" HorizontalAlignment="Stretch" />
<Button x:Name="RemoveButton" Content="Remove Current" HorizontalAlignment="Stretch" />
<Button x:Name="ShuffleButton" Content="Shuffle" HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText" Text="Item: 1 / 4"
Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1" CornerRadius="6" ClipToBounds="True">
<Carousel x:Name="DemoCarousel"
Height="280"
IsSwipeEnabled="True">
<Carousel.PageTransition>
<CrossFade Duration="0.3" />
</Carousel.PageTransition>
<Carousel.ItemTemplate>
<DataTemplate x:DataType="pages:CarouselCardItem">
<Border CornerRadius="14" Margin="14,12" ClipToBounds="True"
Background="{Binding Background}">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="{Binding Number}" FontSize="52" FontWeight="Bold"
Foreground="White" HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="{Binding Title}" FontSize="15" FontWeight="SemiBold"
Foreground="{Binding Accent}" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</DataTemplate>
</Carousel.ItemTemplate>
</Carousel>
</Border>
</DockPanel>
</UserControl>

95
samples/ControlCatalog/Pages/CarouselPage/CarouselDataBindingPage.xaml.cs

@ -0,0 +1,95 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public class CarouselCardItem
{
public string Number { get; set; } = "";
public string Title { get; set; } = "";
public IBrush Background { get; set; } = Brushes.Gray;
public IBrush Accent { get; set; } = Brushes.White;
}
public partial class CarouselDataBindingPage : UserControl
{
private static readonly (string Title, string Color, string Accent)[] Palette =
{
("Neon Pulse", "#3525CD", "#C3C0FF"), ("Ephemeral Blue", "#0891B2", "#BAF0FA"),
("Forest Forms", "#059669", "#A7F3D0"), ("Golden Hour", "#D97706", "#FDE68A"),
("Crimson Wave", "#BE185D", "#FBCFE8"), ("Stone Age", "#57534E", "#D6D3D1"),
};
private readonly ObservableCollection<CarouselCardItem> _items = new();
private int _addCounter;
public CarouselDataBindingPage()
{
InitializeComponent();
DemoCarousel.ItemsSource = _items;
DemoCarousel.SelectionChanged += OnSelectionChanged;
for (var i = 0; i < 4; i++)
AppendItem();
PreviousButton.Click += (_, _) => DemoCarousel.Previous();
NextButton.Click += (_, _) => DemoCarousel.Next();
AddButton.Click += OnAddItem;
RemoveButton.Click += OnRemoveCurrent;
ShuffleButton.Click += OnShuffle;
UpdateStatus();
}
private void AppendItem()
{
var (title, color, accent) = Palette[_addCounter % Palette.Length];
_items.Add(new CarouselCardItem
{
Number = $"{_items.Count + 1:D2}",
Title = title,
Background = new SolidColorBrush(Color.Parse(color)),
Accent = new SolidColorBrush(Color.Parse(accent)),
});
_addCounter++;
}
private void OnAddItem(object? sender, RoutedEventArgs e)
{
AppendItem();
UpdateStatus();
}
private void OnRemoveCurrent(object? sender, RoutedEventArgs e)
{
if (_items.Count == 0)
return;
var idx = Math.Clamp(DemoCarousel.SelectedIndex, 0, _items.Count - 1);
_items.RemoveAt(idx);
UpdateStatus();
}
private void OnShuffle(object? sender, RoutedEventArgs e)
{
var rng = new Random();
var shuffled = _items.OrderBy(_ => rng.Next()).ToList();
_items.Clear();
foreach (var item in shuffled)
_items.Add(item);
UpdateStatus();
}
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
UpdateStatus();
}
private void UpdateStatus()
{
StatusText.Text = $"Item: {DemoCarousel.SelectedIndex + 1} / {_items.Count}";
}
}
}

557
samples/ControlCatalog/Pages/CarouselPage/CarouselGalleryAppPage.xaml

@ -0,0 +1,557 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselGalleryAppPage"
Background="#F8F9FB">
<UserControl.Resources>
<!-- White pip colors for the hero dark background -->
<SolidColorBrush x:Key="PipsPagerSelectionIndicatorForeground" Color="#7FFFFFFF" />
<SolidColorBrush x:Key="PipsPagerSelectionIndicatorForegroundPointerOver" Color="#BFFFFFFF" />
<SolidColorBrush x:Key="PipsPagerSelectionIndicatorForegroundPressed" Color="#BFFFFFFF" />
<SolidColorBrush x:Key="PipsPagerSelectionIndicatorForegroundSelected" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="PipsPagerSelectionIndicatorForegroundDisabled" Color="#3FFFFFFF" />
<SolidColorBrush x:Key="PipsPagerNavigationButtonForeground" Color="#7FFFFFFF" />
<SolidColorBrush x:Key="PipsPagerNavigationButtonForegroundPointerOver" Color="#BFFFFFFF" />
<SolidColorBrush x:Key="PipsPagerNavigationButtonForegroundPressed" Color="#BFFFFFFF" />
</UserControl.Resources>
<DockPanel>
<!-- Right info panel — visible when width >= 640px -->
<ScrollViewer x:Name="InfoPanel" DockPanel.Dock="Right" Width="290" IsVisible="False">
<StackPanel Margin="16" Spacing="16">
<TextBlock Text="Curated Gallery" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.7"
Text="Art gallery editorial app showcasing a full-bleed hero Carousel synced with a pill-shaped PipsPager, a peek Collection Highlights scroll list, Curators' Choice cards, and a Join the Circle subscription section. Navigation via DrawerPage side menu." />
<Separator />
<TextBlock Text="Navigation Flow" FontSize="13" FontWeight="SemiBold" />
<StackPanel Spacing="4">
<TextBlock FontSize="12" TextWrapping="Wrap" Text="1. DrawerPage: root, side placement" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="2. Hamburger overlaid on hero opens the drawer pane" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="3. Hero: full-bleed Carousel + PipsPager (pill dots)" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="4. PipsPager synced bidirectionally with Carousel" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="5. Mouse drag on hero navigates carousel slides" />
</StackPanel>
<Separator />
<TextBlock Text="Key Code" FontSize="13" FontWeight="SemiBold" />
<Border Background="{DynamicResource SystemControlBackgroundBaseLowBrush}"
CornerRadius="4" Padding="8">
<TextBlock FontFamily="Cascadia Code,Consolas,Menlo,monospace"
FontSize="10" TextWrapping="Wrap"
Text="HeroCarousel.SelectionChanged&#xA; += OnHeroSelectionChanged;&#xA;HeroPager.SelectedIndexChanged&#xA; += OnPagerIndexChanged;&#xA;&#xA;// Bidirectional sync guard&#xA;if (_syncing) return;&#xA;_syncing = true;&#xA;HeroPager.SelectedPageIndex&#xA; = HeroCarousel.SelectedIndex;&#xA;_syncing = false;" />
</Border>
</StackPanel>
</ScrollViewer>
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1" CornerRadius="8" ClipToBounds="True">
<DrawerPage x:Name="RootDrawer"
DrawerLength="260"
IsGestureEnabled="True">
<DrawerPage.Styles>
<!-- Hide the DrawerPage built-in top bar so only our custom hero overlay bar is shown -->
<Style Selector="DrawerPage#RootDrawer /template/ Border#PART_TopBar">
<Setter Property="IsVisible" Value="False" />
</Style>
</DrawerPage.Styles>
<!-- Drawer header -->
<DrawerPage.DrawerHeader>
<Border Background="#3525CD" Padding="20,32,20,20">
<StackPanel Spacing="4">
<TextBlock Text="CURATED"
FontSize="20"
FontWeight="Bold"
Foreground="White"
LetterSpacing="-0.4" />
<TextBlock Text="The Digital Gallery"
FontSize="12"
Foreground="#C3C0FF"
Opacity="0.85" />
</StackPanel>
</Border>
</DrawerPage.DrawerHeader>
<!-- Drawer menu -->
<DrawerPage.Drawer>
<StackPanel Background="#F8F9FB">
<ListBox x:Name="DrawerMenu"
Background="Transparent"
SelectionChanged="OnDrawerMenuSelectionChanged">
<ListBoxItem Padding="20,14">
<StackPanel Orientation="Horizontal" Spacing="16">
<Path Data="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
Fill="#3525CD" Width="20" Height="20" Stretch="Uniform" />
<TextBlock Text="Discover" FontSize="15" FontWeight="SemiBold"
Foreground="#191C1E" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem Padding="20,14">
<StackPanel Orientation="Horizontal" Spacing="16">
<Path Data="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"
Fill="#464555" Width="20" Height="20" Stretch="Uniform" />
<TextBlock Text="Collection" FontSize="15"
Foreground="#464555" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem Padding="20,14">
<StackPanel Orientation="Horizontal" Spacing="16">
<Path Data="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 14l-5-5 1.41-1.41L12 14.17l7.59-7.59L21 8l-9 9z"
Fill="#464555" Width="20" Height="20" Stretch="Uniform" />
<TextBlock Text="Archive" FontSize="15"
Foreground="#464555" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem Padding="20,14">
<StackPanel Orientation="Horizontal" Spacing="16">
<Path Data="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"
Fill="#464555" Width="20" Height="20" Stretch="Uniform" />
<TextBlock Text="Profile" FontSize="15"
Foreground="#464555" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
</ListBox>
<Separator Margin="20,8" />
<StackPanel Margin="20,8" Spacing="0">
<TextBlock Text="EXHIBITIONS"
FontSize="11"
FontWeight="Bold"
Foreground="#777587"
LetterSpacing="1.2"
Margin="0,0,0,16" />
<Grid ColumnDefinitions="4,*" Margin="0,0,0,14">
<Border Grid.Column="0" Width="4" Height="38"
CornerRadius="2" Background="#3525CD"
VerticalAlignment="Center" Margin="0,0,14,0" />
<StackPanel Grid.Column="1" Spacing="2" VerticalAlignment="Center">
<TextBlock Text="Neon Pulse" FontSize="14" FontWeight="SemiBold"
Foreground="#191C1E" />
<TextBlock Text="Opens March 20" FontSize="11" Foreground="#777587" />
</StackPanel>
</Grid>
<Grid ColumnDefinitions="4,*" Margin="0,0,0,14">
<Border Grid.Column="0" Width="4" Height="38"
CornerRadius="2" Background="#4F46E5"
VerticalAlignment="Center" Margin="0,0,14,0" />
<StackPanel Grid.Column="1" Spacing="2" VerticalAlignment="Center">
<TextBlock Text="Fragmented Forms" FontSize="14" FontWeight="SemiBold"
Foreground="#191C1E" />
<TextBlock Text="Now Open" FontSize="11" Foreground="#4F46E5" />
</StackPanel>
</Grid>
<Grid ColumnDefinitions="4,*">
<Border Grid.Column="0" Width="4" Height="38"
CornerRadius="2" Background="#B84B00"
VerticalAlignment="Center" Margin="0,0,14,0" />
<StackPanel Grid.Column="1" Spacing="2" VerticalAlignment="Center">
<TextBlock Text="The Digital Horizon" FontSize="14" FontWeight="SemiBold"
Foreground="#191C1E" />
<TextBlock Text="Closing Soon" FontSize="11" Foreground="#B84B00" />
</StackPanel>
</Grid>
</StackPanel>
</StackPanel>
</DrawerPage.Drawer>
<!-- Main content: hero carousel IS the header -->
<DrawerPage.Content>
<Grid RowDefinitions="Auto,*">
<!-- Row 0: Hero carousel header — also handles mouse drag for swipe navigation -->
<Grid Height="320"
PointerPressed="OnHeroPointerPressed"
PointerReleased="OnHeroPointerReleased"
PointerCaptureLost="OnHeroPointerCaptureLost">
<!-- Full-bleed hero carousel -->
<Carousel x:Name="HeroCarousel"
IsSwipeEnabled="True">
<Carousel.PageTransition>
<PageSlide Duration="0.35" Orientation="Horizontal" />
</Carousel.PageTransition>
<!-- Hero 1 -->
<Grid>
<Image Source="/Assets/ModernApp/gallery_city.jpg" Stretch="UniformToFill" />
<Border>
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#88000000" Offset="0" />
<GradientStop Color="#00000000" Offset="0.35" />
<GradientStop Color="#00000000" Offset="0.55" />
<GradientStop Color="#CC000000" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
<StackPanel VerticalAlignment="Bottom" Margin="20,0,20,44" Spacing="4">
<TextBlock Text="FEATURED EXHIBITION"
FontSize="11" FontWeight="Bold"
Foreground="#C3C0FF" LetterSpacing="1.5" />
<TextBlock Text="Neon Pulse: The New Abstract"
FontSize="22" FontWeight="Bold"
Foreground="White" TextWrapping="Wrap" LetterSpacing="-0.4" />
</StackPanel>
</Grid>
<!-- Hero 2 -->
<Grid>
<Image Source="/Assets/ModernApp/gallery_alpine.jpg" Stretch="UniformToFill" />
<Border>
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#88000000" Offset="0" />
<GradientStop Color="#00000000" Offset="0.35" />
<GradientStop Color="#00000000" Offset="0.55" />
<GradientStop Color="#CC000000" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
<StackPanel VerticalAlignment="Bottom" Margin="20,0,20,44" Spacing="4">
<TextBlock Text="NOW OPEN"
FontSize="11" FontWeight="Bold"
Foreground="#C3C0FF" LetterSpacing="1.5" />
<TextBlock Text="Fragmented Forms: Sculpture Today"
FontSize="22" FontWeight="Bold"
Foreground="White" TextWrapping="Wrap" LetterSpacing="-0.4" />
</StackPanel>
</Grid>
<!-- Hero 3 -->
<Grid>
<Image Source="/Assets/ModernApp/gallery_venice.jpg" Stretch="UniformToFill" />
<Border>
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#88000000" Offset="0" />
<GradientStop Color="#00000000" Offset="0.35" />
<GradientStop Color="#00000000" Offset="0.55" />
<GradientStop Color="#CC000000" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
<StackPanel VerticalAlignment="Bottom" Margin="20,0,20,44" Spacing="4">
<TextBlock Text="CLOSING SOON"
FontSize="11" FontWeight="Bold"
Foreground="#FFCDD2" LetterSpacing="1.5" />
<TextBlock Text="The Digital Horizon: Web3 &amp; Generative Art"
FontSize="22" FontWeight="Bold"
Foreground="White" TextWrapping="Wrap" LetterSpacing="-0.4" />
</StackPanel>
</Grid>
</Carousel>
<!-- PipsPager overlaid near bottom of hero — pill-shaped, no nav arrows -->
<PipsPager x:Name="HeroPager"
NumberOfPages="3"
IsPreviousButtonVisible="False"
IsNextButtonVisible="False"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,18">
<PipsPager.Styles>
<Style Selector="PipsPager /template/ ListBox ListBoxItem">
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="2,0" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="ClipToBounds" Value="False" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Grid Background="Transparent">
<Border Name="Pip"
Width="6" Height="6" CornerRadius="3"
HorizontalAlignment="Center" VerticalAlignment="Center"
Background="#7FFFFFFF">
<Border.Transitions>
<Transitions>
<DoubleTransition Property="Width" Duration="0:0:0.2" Easing="CubicEaseOut" />
<CornerRadiusTransition Property="CornerRadius" Duration="0:0:0.2" Easing="CubicEaseOut" />
<BrushTransition Property="Background" Duration="0:0:0.2" />
</Transitions>
</Border.Transitions>
</Border>
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="PipsPager /template/ ListBox ListBoxItem:pointerover /template/ Border#Pip">
<Setter Property="Width" Value="8" />
<Setter Property="Height" Value="8" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="Background" Value="#BFFFFFFF" />
</Style>
<Style Selector="PipsPager /template/ ListBox ListBoxItem:selected /template/ Border#Pip">
<Setter Property="Width" Value="22" />
<Setter Property="Height" Value="6" />
<Setter Property="CornerRadius" Value="3" />
<Setter Property="Background" Value="#FFFFFFFF" />
</Style>
<Style Selector="PipsPager /template/ ListBox ListBoxItem:selected:pointerover /template/ Border#Pip">
<Setter Property="Width" Value="22" />
<Setter Property="Height" Value="6" />
<Setter Property="CornerRadius" Value="3" />
<Setter Property="Background" Value="#E8FFFFFF" />
</Style>
</PipsPager.Styles>
</PipsPager>
<!-- Top bar overlaid on hero -->
<Grid ColumnDefinitions="Auto,*,Auto"
VerticalAlignment="Top"
Margin="4,8,4,0">
<Button Grid.Column="0"
Background="Transparent"
BorderThickness="0"
Padding="12,8"
Click="OnHamburgerClick">
<Path Data="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"
Fill="White" Width="22" Height="22" Stretch="Uniform" />
</Button>
<TextBlock Grid.Column="1"
Text="Curated"
FontSize="18"
FontWeight="Bold"
Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center"
LetterSpacing="-0.3" />
<Button Grid.Column="2"
Background="Transparent"
BorderThickness="0"
Padding="12,8">
<Path Data="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
Fill="White" Width="22" Height="22" Stretch="Uniform" />
</Button>
</Grid>
</Grid>
<!-- Row 1: Scrollable body -->
<ScrollViewer Grid.Row="1"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel>
<!-- Collection Highlights -->
<StackPanel Margin="0,28,0,0">
<Grid ColumnDefinitions="*,Auto" Margin="20,0,20,16">
<TextBlock Text="Collection Highlights"
FontSize="18" FontWeight="Bold"
Foreground="#191C1E" LetterSpacing="-0.3" />
<TextBlock Grid.Column="1"
Text="SEE ALL"
FontSize="12" FontWeight="Bold"
Foreground="#3525CD" LetterSpacing="0.8"
VerticalAlignment="Center" />
</Grid>
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Disabled"
Margin="20,0,0,0">
<StackPanel Orientation="Horizontal" Spacing="10">
<Border Width="180" Height="210" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/ModernApp/gallery_paris.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12,10">
<StackPanel Spacing="2">
<TextBlock Text="SCULPTURE" FontSize="10" FontWeight="Bold"
Foreground="#C3C0FF" LetterSpacing="1" />
<TextBlock Text="Fragmented Grace" FontSize="13"
FontWeight="SemiBold" Foreground="White" />
</StackPanel>
</Border>
</Grid>
</Border>
<Border Width="180" Height="210" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/ModernApp/gallery_bay.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12,10">
<StackPanel Spacing="2">
<TextBlock Text="OIL PAINTING" FontSize="10" FontWeight="Bold"
Foreground="#C3C0FF" LetterSpacing="1" />
<TextBlock Text="Ephemeral Blue" FontSize="13"
FontWeight="SemiBold" Foreground="White" />
</StackPanel>
</Border>
</Grid>
</Border>
<Border Width="180" Height="210" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/ModernApp/gallery_tropical.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12,10">
<StackPanel Spacing="2">
<TextBlock Text="TEXTILE" FontSize="10" FontWeight="Bold"
Foreground="#C3C0FF" LetterSpacing="1" />
<TextBlock Text="Interwoven Lines" FontSize="13"
FontWeight="SemiBold" Foreground="White" />
</StackPanel>
</Border>
</Grid>
</Border>
<Border Width="180" Height="210" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/ModernApp/gallery_alpine.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12,10">
<StackPanel Spacing="2">
<TextBlock Text="PHOTOGRAPHY" FontSize="10" FontWeight="Bold"
Foreground="#C3C0FF" LetterSpacing="1" />
<TextBlock Text="Silent Mountains" FontSize="13"
FontWeight="SemiBold" Foreground="White" />
</StackPanel>
</Border>
</Grid>
</Border>
<!-- Padding card to reveal peek of last item -->
<Border Width="20" Height="210" />
</StackPanel>
</ScrollViewer>
</StackPanel>
<!-- Curators' Choice -->
<StackPanel Margin="20,32,20,0" Spacing="12">
<TextBlock Text="Curators' Choice"
FontSize="20" FontWeight="Bold"
Foreground="#191C1E" HorizontalAlignment="Center"
LetterSpacing="-0.3" />
<TextBlock Text="Hand-picked selections from our global network of artists."
FontSize="13" Foreground="#777587"
HorizontalAlignment="Center" TextAlignment="Center"
TextWrapping="Wrap" Margin="0,0,0,8" />
<!-- Two-column layout: large card left, two stacked badge cards right -->
<Grid ColumnDefinitions="*,130">
<!-- Left: main feature card -->
<Border Grid.Column="0" Background="White" CornerRadius="16"
Padding="20" Margin="0,0,10,0"
BoxShadow="0 2 16 0 #12191C1E">
<StackPanel Spacing="10">
<Path Data="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5z"
Fill="#3525CD" Width="22" Height="22" Stretch="Uniform"
HorizontalAlignment="Left" />
<TextBlock Text="The Digital Horizon"
FontSize="17" FontWeight="Bold"
Foreground="#191C1E" TextWrapping="Wrap" />
<TextBlock Text="Exploring Web3 and Generative Art"
FontSize="13" Foreground="#777587" TextWrapping="Wrap" />
<Button Content="EXPLORE"
Margin="0,10,0,0" Padding="20,11"
FontSize="11" FontWeight="Bold" LetterSpacing="0.8"
CornerRadius="22" Foreground="White" HorizontalAlignment="Left">
<Button.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#3525CD" Offset="0" />
<GradientStop Color="#4F46E5" Offset="1" />
</LinearGradientBrush>
</Button.Background>
</Button>
</StackPanel>
</Border>
<!-- Right: two stacked badge cards -->
<StackPanel Grid.Column="1" Spacing="10">
<Border Background="White" CornerRadius="16"
BoxShadow="0 2 16 0 #12191C1E"
Padding="12">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center"
Spacing="8" Margin="0,12">
<Path Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z"
Fill="#B84B00" Width="28" Height="28" Stretch="Uniform"
HorizontalAlignment="Center" />
<TextBlock Text="TRENDING"
FontSize="10" FontWeight="Bold"
Foreground="#B84B00" LetterSpacing="1"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Background="#EDEEF0" CornerRadius="16" Padding="12">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center"
Spacing="8" Margin="0,12">
<Path Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1.41 14.06L6.17 11.64l1.42-1.41 2.99 3 6.01-6.01 1.42 1.41-7.42 7.43z"
Fill="#464555" Width="28" Height="28" Stretch="Uniform"
HorizontalAlignment="Center" />
<TextBlock Text="VERIFIED"
FontSize="10" FontWeight="Bold"
Foreground="#464555" LetterSpacing="1"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</StackPanel>
</Grid>
</StackPanel>
<!-- Join the Circle -->
<Border Margin="20,32,20,32" CornerRadius="20" Padding="24" ClipToBounds="True">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#3525CD" Offset="0" />
<GradientStop Color="#4F46E5" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel Spacing="10">
<TextBlock Text="Join the Circle"
FontSize="20" FontWeight="Bold"
Foreground="White" LetterSpacing="-0.3" />
<TextBlock Text="Receive exclusive invitations to private viewings and new drop alerts."
FontSize="13" Foreground="#C3C0FF"
TextWrapping="Wrap" Opacity="0.9" LineHeight="20" />
<TextBox PlaceholderText="Your email address"
Margin="0,6,0,0"
CornerRadius="12"
BorderThickness="1"
Padding="14,12"
Foreground="White"
PlaceholderForeground="#9896D8">
<TextBox.Resources>
<SolidColorBrush x:Key="TextControlBackground" Color="#3C38B5" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#4440BE" />
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#3C38B5" />
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#5552C8" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#7370D8" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#8B88E8" />
</TextBox.Resources>
</TextBox>
<Button Content="SUBSCRIBE"
Margin="0,2,0,0" Padding="24,12"
FontSize="12" FontWeight="Bold" LetterSpacing="1"
CornerRadius="24" Foreground="#3525CD" Background="White"
HorizontalAlignment="Left" />
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Grid>
</DrawerPage.Content>
</DrawerPage>
</Border>
</DockPanel>
</UserControl>

101
samples/ControlCatalog/Pages/CarouselPage/CarouselGalleryAppPage.xaml.cs

@ -0,0 +1,101 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselGalleryAppPage : UserControl
{
private bool _syncing;
private Point _dragStart;
private bool _isDragging;
private const double SwipeThreshold = 50;
private ScrollViewer? _infoPanel;
public CarouselGalleryAppPage()
{
InitializeComponent();
_infoPanel = this.FindControl<ScrollViewer>("InfoPanel");
HeroCarousel.SelectionChanged += OnHeroSelectionChanged;
HeroPager.SelectedIndexChanged += OnPagerIndexChanged;
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
UpdateInfoPanelVisibility();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == BoundsProperty)
UpdateInfoPanelVisibility();
}
private void UpdateInfoPanelVisibility()
{
if (_infoPanel != null)
_infoPanel.IsVisible = Bounds.Width >= 640;
}
private void OnHamburgerClick(object? sender, RoutedEventArgs e)
{
RootDrawer.IsOpen = !RootDrawer.IsOpen;
}
private void OnHeroSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (_syncing)
return;
_syncing = true;
HeroPager.SelectedPageIndex = HeroCarousel.SelectedIndex;
_syncing = false;
}
private void OnPagerIndexChanged(object? sender, PipsPagerSelectedIndexChangedEventArgs e)
{
if (_syncing)
return;
_syncing = true;
HeroCarousel.SelectedIndex = e.NewIndex;
_syncing = false;
}
private void OnDrawerMenuSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
RootDrawer.IsOpen = false;
DrawerMenu.SelectedItem = null;
}
private void OnHeroPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(null).Properties.IsLeftButtonPressed)
return;
_dragStart = e.GetPosition((Visual?)sender);
_isDragging = true;
}
private void OnHeroPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (!_isDragging)
return;
_isDragging = false;
var delta = e.GetPosition((Visual?)sender).X - _dragStart.X;
if (Math.Abs(delta) < SwipeThreshold)
return;
if (delta < 0)
HeroCarousel.Next();
else
HeroCarousel.Previous();
}
private void OnHeroPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
{
_isDragging = false;
}
}
}

93
samples/ControlCatalog/Pages/CarouselPage/CarouselGesturesPage.xaml

@ -0,0 +1,93 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselGesturesPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Options" FontWeight="SemiBold" FontSize="13" />
<CheckBox x:Name="SwipeCheck"
Content="Swipe Gesture"
IsChecked="True"
IsCheckedChanged="OnSwipeEnabledChanged" />
<CheckBox x:Name="WrapCheck"
Content="Wrap Selection"
IsChecked="False"
IsCheckedChanged="OnWrapSelectionChanged" />
<CheckBox x:Name="KeyboardCheck"
Content="Keyboard Navigation"
IsChecked="True"
IsCheckedChanged="OnKeyboardEnabledChanged" />
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText"
Text="Item: 1 / 3"
Opacity="0.7"
TextWrapping="Wrap" />
<TextBlock x:Name="LastActionText"
Text="Action: —"
Opacity="0.7"
TextWrapping="Wrap" />
<TextBlock Text="Swipe left/right or use arrow keys to navigate."
FontSize="11"
Opacity="0.5"
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<Carousel x:Name="DemoCarousel"
Focusable="True"
IsSwipeEnabled="True"
Height="300">
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Horizontal" />
</Carousel.PageTransition>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/delicate-arch-896885_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 1: Delicate Arch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/hirsch-899118_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 2: Hirsch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/maple-leaf-888807_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 3: Maple Leaf" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
</Carousel>
</Border>
</DockPanel>
</UserControl>

59
samples/ControlCatalog/Pages/CarouselPage/CarouselGesturesPage.xaml.cs

@ -0,0 +1,59 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselGesturesPage : UserControl
{
private bool _keyboardEnabled = true;
public CarouselGesturesPage()
{
InitializeComponent();
DemoCarousel.AddHandler(InputElement.KeyDownEvent, OnKeyDown, handledEventsToo: true);
DemoCarousel.SelectionChanged += OnSelectionChanged;
DemoCarousel.Loaded += (_, _) => DemoCarousel.Focus();
}
private void OnKeyDown(object? sender, KeyEventArgs e)
{
if (!_keyboardEnabled)
return;
switch (e.Key)
{
case Key.Left:
case Key.Up:
LastActionText.Text = $"Action: Key {e.Key} (Previous)";
break;
case Key.Right:
case Key.Down:
LastActionText.Text = $"Action: Key {e.Key} (Next)";
break;
}
}
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
StatusText.Text = $"Item: {DemoCarousel.SelectedIndex + 1} / {DemoCarousel.ItemCount}";
if (DemoCarousel.IsSwiping)
LastActionText.Text = "Action: Swipe";
}
private void OnSwipeEnabledChanged(object? sender, RoutedEventArgs e)
{
DemoCarousel.IsSwipeEnabled = SwipeCheck.IsChecked == true;
}
private void OnWrapSelectionChanged(object? sender, RoutedEventArgs e)
{
DemoCarousel.WrapSelection = WrapCheck.IsChecked == true;
}
private void OnKeyboardEnabledChanged(object? sender, RoutedEventArgs e)
{
_keyboardEnabled = KeyboardCheck.IsChecked == true;
}
}
}

74
samples/ControlCatalog/Pages/CarouselPage/CarouselGettingStartedPage.xaml

@ -0,0 +1,74 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselGettingStartedPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontWeight="SemiBold" FontSize="13" />
<Button x:Name="PreviousButton"
Content="Previous"
HorizontalAlignment="Stretch" />
<Button x:Name="NextButton"
Content="Next"
HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText"
Text="Item: 1 / 3"
Opacity="0.7"
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<Carousel x:Name="DemoCarousel" Height="300" IsSwipeEnabled="True">
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Horizontal" />
</Carousel.PageTransition>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/delicate-arch-896885_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 1: Delicate Arch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/hirsch-899118_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 2: Hirsch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/maple-leaf-888807_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 3: Maple Leaf" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
</Carousel>
</Border>
</DockPanel>
</UserControl>

40
samples/ControlCatalog/Pages/CarouselPage/CarouselGettingStartedPage.xaml.cs

@ -0,0 +1,40 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselGettingStartedPage : UserControl
{
public CarouselGettingStartedPage()
{
InitializeComponent();
PreviousButton.Click += OnPrevious;
NextButton.Click += OnNext;
DemoCarousel.SelectionChanged += OnSelectionChanged;
}
private void OnPrevious(object? sender, RoutedEventArgs e)
{
DemoCarousel.Previous();
UpdateStatus();
}
private void OnNext(object? sender, RoutedEventArgs e)
{
DemoCarousel.Next();
UpdateStatus();
}
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
UpdateStatus();
}
private void UpdateStatus()
{
var index = DemoCarousel.SelectedIndex + 1;
var count = DemoCarousel.ItemCount;
StatusText.Text = $"Item: {index} / {count}";
}
}
}

140
samples/ControlCatalog/Pages/CarouselPage/CarouselMultiItemPage.xaml

@ -0,0 +1,140 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselMultiItemPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontWeight="SemiBold" FontSize="13" />
<Button x:Name="PreviousButton" Content="Previous" HorizontalAlignment="Stretch" />
<Button x:Name="NextButton" Content="Next" HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Viewport Fraction" FontWeight="SemiBold" FontSize="13" />
<TextBlock TextWrapping="Wrap" FontSize="11" Opacity="0.6"
Text="Values below 1.0 show adjacent items peeking into the viewport." />
<Grid ColumnDefinitions="*,48" ColumnSpacing="8">
<Slider x:Name="ViewportSlider"
Minimum="0.2" Maximum="1.0" Value="0.5"
TickFrequency="0.01" IsSnapToTickEnabled="True"
HorizontalAlignment="Stretch"
ValueChanged="OnViewportFractionChanged" />
<TextBlock x:Name="ViewportLabel" Grid.Column="1"
Text="0.50" VerticalAlignment="Center"
HorizontalAlignment="Right" FontWeight="SemiBold" />
</Grid>
<TextBlock x:Name="ViewportHint"
Text="~2 items visible."
FontSize="11" Opacity="0.6" TextWrapping="Wrap" />
<Separator />
<TextBlock Text="Options" FontWeight="SemiBold" FontSize="13" />
<CheckBox x:Name="WrapCheck" Content="Wrap Selection" IsChecked="False"
IsCheckedChanged="OnWrapChanged" />
<CheckBox x:Name="SwipeCheck" Content="Swipe / Drag" IsChecked="True"
IsCheckedChanged="OnSwipeChanged" />
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText" Text="Item: 1 / 5"
Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1" CornerRadius="6" ClipToBounds="True">
<Carousel x:Name="DemoCarousel"
Height="280"
ViewportFraction="0.5"
IsSwipeEnabled="True">
<Carousel.PageTransition>
<PageSlide Duration="0.3" Orientation="Horizontal" />
</Carousel.PageTransition>
<Border Margin="6,12" CornerRadius="14" ClipToBounds="True">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#3525CD" Offset="0" />
<GradientStop Color="#6B5CE7" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="01" FontSize="52" FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Neon Pulse" FontSize="15" FontWeight="SemiBold"
Foreground="#C3C0FF" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="6,12" CornerRadius="14" ClipToBounds="True">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#0891B2" Offset="0" />
<GradientStop Color="#06B6D4" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="02" FontSize="52" FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Ephemeral Blue" FontSize="15" FontWeight="SemiBold"
Foreground="#BAF0FA" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="6,12" CornerRadius="14" ClipToBounds="True">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#059669" Offset="0" />
<GradientStop Color="#10B981" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="03" FontSize="52" FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Forest Forms" FontSize="15" FontWeight="SemiBold"
Foreground="#A7F3D0" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="6,12" CornerRadius="14" ClipToBounds="True">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#D97706" Offset="0" />
<GradientStop Color="#F59E0B" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="04" FontSize="52" FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Golden Hour" FontSize="15" FontWeight="SemiBold"
Foreground="#FDE68A" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="6,12" CornerRadius="14" ClipToBounds="True">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#BE185D" Offset="0" />
<GradientStop Color="#EC4899" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="05" FontSize="52" FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Crimson Wave" FontSize="15" FontWeight="SemiBold"
Foreground="#FBCFE8" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Carousel>
</Border>
</DockPanel>
</UserControl>

47
samples/ControlCatalog/Pages/CarouselPage/CarouselMultiItemPage.xaml.cs

@ -0,0 +1,47 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselMultiItemPage : UserControl
{
public CarouselMultiItemPage()
{
InitializeComponent();
PreviousButton.Click += (_, _) => DemoCarousel.Previous();
NextButton.Click += (_, _) => DemoCarousel.Next();
DemoCarousel.SelectionChanged += OnSelectionChanged;
}
private void OnViewportFractionChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
if (DemoCarousel is null)
return;
var value = Math.Round(e.NewValue, 2);
DemoCarousel.ViewportFraction = value;
ViewportLabel.Text = value.ToString("0.00");
ViewportHint.Text = value >= 1d ? "1.00 — single full item." : $"~{1d / value:0.#} items visible.";
}
private void OnWrapChanged(object? sender, RoutedEventArgs e)
{
if (DemoCarousel is null)
return;
DemoCarousel.WrapSelection = WrapCheck.IsChecked == true;
}
private void OnSwipeChanged(object? sender, RoutedEventArgs e)
{
if (DemoCarousel is null)
return;
DemoCarousel.IsSwipeEnabled = SwipeCheck.IsChecked == true;
}
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
StatusText.Text = $"Item: {DemoCarousel.SelectedIndex + 1} / {DemoCarousel.ItemCount}";
}
}
}

97
samples/ControlCatalog/Pages/CarouselPage/CarouselTransitionsPage.xaml

@ -0,0 +1,97 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselTransitionsPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontWeight="SemiBold" FontSize="13" />
<Button x:Name="PreviousButton"
Content="Previous"
HorizontalAlignment="Stretch" />
<Button x:Name="NextButton"
Content="Next"
HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Transition" FontWeight="SemiBold" FontSize="13" />
<ComboBox x:Name="TransitionCombo"
HorizontalAlignment="Stretch"
SelectedIndex="1">
<ComboBoxItem>None</ComboBoxItem>
<ComboBoxItem>Page Slide</ComboBoxItem>
<ComboBoxItem>Cross Fade</ComboBoxItem>
<ComboBoxItem>Rotate 3D</ComboBoxItem>
<ComboBoxItem>Card Stack</ComboBoxItem>
<ComboBoxItem>Wave Reveal</ComboBoxItem>
<ComboBoxItem>Composite (Slide + Fade)</ComboBoxItem>
</ComboBox>
<TextBlock Text="Orientation" FontWeight="SemiBold" FontSize="13" />
<ComboBox x:Name="OrientationCombo"
HorizontalAlignment="Stretch"
SelectedIndex="0">
<ComboBoxItem>Horizontal</ComboBoxItem>
<ComboBoxItem>Vertical</ComboBoxItem>
</ComboBox>
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText"
Text="Transition: Page Slide"
Opacity="0.7"
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<Carousel x:Name="DemoCarousel" Height="300">
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Horizontal" />
</Carousel.PageTransition>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/delicate-arch-896885_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 1: Delicate Arch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/hirsch-899118_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 2: Hirsch" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
<Border Margin="14,12" CornerRadius="12" ClipToBounds="True">
<Grid>
<Image Source="/Assets/maple-leaf-888807_640.jpg" Stretch="UniformToFill" />
<Border Background="#80000000" VerticalAlignment="Bottom" Padding="12">
<TextBlock Text="Item 3: Maple Leaf" Foreground="White"
HorizontalAlignment="Center" FontWeight="SemiBold" />
</Border>
</Grid>
</Border>
</Carousel>
</Border>
</DockPanel>
</UserControl>

66
samples/ControlCatalog/Pages/CarouselPage/CarouselTransitionsPage.xaml.cs

@ -0,0 +1,66 @@
using System;
using Avalonia.Animation;
using Avalonia.Controls;
using ControlCatalog.Pages.Transitions;
namespace ControlCatalog.Pages
{
public partial class CarouselTransitionsPage : UserControl
{
public CarouselTransitionsPage()
{
InitializeComponent();
PreviousButton.Click += (_, _) => DemoCarousel.Previous();
NextButton.Click += (_, _) => DemoCarousel.Next();
TransitionCombo.SelectionChanged += (_, _) => ApplyTransition();
OrientationCombo.SelectionChanged += (_, _) => ApplyTransition();
}
private void ApplyTransition()
{
var axis = OrientationCombo.SelectedIndex == 0 ?
PageSlide.SlideAxis.Horizontal :
PageSlide.SlideAxis.Vertical;
var label = axis == PageSlide.SlideAxis.Horizontal ? "Horizontal" : "Vertical";
switch (TransitionCombo.SelectedIndex)
{
case 0:
DemoCarousel.PageTransition = null;
StatusText.Text = "Transition: None";
break;
case 1:
DemoCarousel.PageTransition = new PageSlide(TimeSpan.FromSeconds(0.25), axis);
StatusText.Text = $"Transition: Page Slide ({label})";
break;
case 2:
DemoCarousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25));
StatusText.Text = "Transition: Cross Fade";
break;
case 3:
DemoCarousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), axis);
StatusText.Text = $"Transition: Rotate 3D ({label})";
break;
case 4:
DemoCarousel.PageTransition = new CardStackPageTransition(TimeSpan.FromSeconds(0.5), axis);
StatusText.Text = $"Transition: Card Stack ({label})";
break;
case 5:
DemoCarousel.PageTransition = new WaveRevealPageTransition(TimeSpan.FromSeconds(0.8), axis);
StatusText.Text = $"Transition: Wave Reveal ({label})";
break;
case 6:
DemoCarousel.PageTransition = new CompositePageTransition
{
PageTransitions =
{
new PageSlide(TimeSpan.FromSeconds(0.25), axis),
new CrossFade(TimeSpan.FromSeconds(0.25)),
}
};
StatusText.Text = "Transition: Composite (Slide + Fade)";
break;
}
}
}
}

132
samples/ControlCatalog/Pages/CarouselPage/CarouselVerticalPage.xaml

@ -0,0 +1,132 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselVerticalPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontWeight="SemiBold" FontSize="13" />
<Button x:Name="PreviousButton" Content="Up" HorizontalAlignment="Stretch" />
<Button x:Name="NextButton" Content="Down" HorizontalAlignment="Stretch" />
<Separator />
<TextBlock Text="Transition" FontWeight="SemiBold" FontSize="13" />
<ComboBox x:Name="TransitionCombo"
HorizontalAlignment="Stretch"
SelectedIndex="0">
<ComboBoxItem>PageSlide</ComboBoxItem>
<ComboBoxItem>CrossFade</ComboBoxItem>
<ComboBoxItem>None</ComboBoxItem>
</ComboBox>
<Separator />
<TextBlock Text="Options" FontWeight="SemiBold" FontSize="13" />
<CheckBox x:Name="WrapCheck"
Content="Wrap Selection"
IsChecked="False"
IsCheckedChanged="OnWrapSelectionChanged" />
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="14" />
<TextBlock x:Name="StatusText"
Text="Item: 1 / 4"
Opacity="0.7"
TextWrapping="Wrap" />
<TextBlock Text="Use Up/Down arrow keys or buttons to navigate."
FontSize="11"
Opacity="0.5"
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1"
Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<Carousel x:Name="DemoCarousel"
Focusable="True"
IsSwipeEnabled="True">
<Carousel.PageTransition>
<PageSlide Duration="0.3" Orientation="Vertical" />
</Carousel.PageTransition>
<Border Margin="14,12" CornerRadius="12">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Color="#1A1A2E" Offset="0" />
<GradientStop Color="#3525CD" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="10">
<TextBlock Text="01" FontSize="64" FontWeight="Bold"
Foreground="White" HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Neon Pulse" FontSize="18" FontWeight="SemiBold"
Foreground="#C3C0FF" HorizontalAlignment="Center" />
<TextBlock Text="Slide down to explore" FontSize="12"
Foreground="#80FFFFFF" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="14,12" CornerRadius="12">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Color="#0C1A1F" Offset="0" />
<GradientStop Color="#0891B2" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="10">
<TextBlock Text="02" FontSize="64" FontWeight="Bold"
Foreground="White" HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Ephemeral Blue" FontSize="18" FontWeight="SemiBold"
Foreground="#BAF0FA" HorizontalAlignment="Center" />
<TextBlock Text="Vertical PageSlide in action" FontSize="12"
Foreground="#80FFFFFF" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="14,12" CornerRadius="12">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Color="#0A1F18" Offset="0" />
<GradientStop Color="#059669" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="10">
<TextBlock Text="03" FontSize="64" FontWeight="Bold"
Foreground="White" HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Forest Forms" FontSize="18" FontWeight="SemiBold"
Foreground="#A7F3D0" HorizontalAlignment="Center" />
<TextBlock Text="Swipe up or down on touch screens" FontSize="12"
Foreground="#80FFFFFF" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Margin="14,12" CornerRadius="12">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Color="#1F1208" Offset="0" />
<GradientStop Color="#D97706" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="10">
<TextBlock Text="04" FontSize="64" FontWeight="Bold"
Foreground="White" HorizontalAlignment="Center" LetterSpacing="-2" />
<TextBlock Text="Golden Hour" FontSize="18" FontWeight="SemiBold"
Foreground="#FDE68A" HorizontalAlignment="Center" />
<TextBlock Text="Switch transitions in the panel" FontSize="12"
Foreground="#80FFFFFF" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Carousel>
</Border>
</DockPanel>
</UserControl>

39
samples/ControlCatalog/Pages/CarouselPage/CarouselVerticalPage.xaml.cs

@ -0,0 +1,39 @@
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CarouselVerticalPage : UserControl
{
public CarouselVerticalPage()
{
InitializeComponent();
PreviousButton.Click += (_, _) => DemoCarousel.Previous();
NextButton.Click += (_, _) => DemoCarousel.Next();
DemoCarousel.SelectionChanged += OnSelectionChanged;
TransitionCombo.SelectionChanged += OnTransitionChanged;
DemoCarousel.Loaded += (_, _) => DemoCarousel.Focus();
}
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
StatusText.Text = $"Item: {DemoCarousel.SelectedIndex + 1} / {DemoCarousel.ItemCount}";
}
private void OnTransitionChanged(object? sender, SelectionChangedEventArgs e)
{
DemoCarousel.PageTransition = TransitionCombo.SelectedIndex switch
{
1 => new CrossFade(System.TimeSpan.FromSeconds(0.3)),
2 => null,
_ => new PageSlide(System.TimeSpan.FromSeconds(0.3), PageSlide.SlideAxis.Vertical),
};
}
private void OnWrapSelectionChanged(object? sender, RoutedEventArgs e)
{
DemoCarousel.WrapSelection = WrapCheck.IsChecked == true;
}
}
}

14
src/Avalonia.Controls/VirtualizingCarouselPanel.cs

@ -440,6 +440,13 @@ namespace Avalonia.Controls
TeardownGestureRecognizer();
}
protected override void OnItemsControlChanged(ItemsControl? oldValue)
{
base.OnItemsControlChanged(oldValue);
RefreshGestureRecognizer();
}
protected override Size MeasureOverride(Size availableSize)
{
if (UsesViewportFractionLayout())
@ -948,7 +955,7 @@ namespace Avalonia.Controls
return;
}
if (_isDragging || _offsetAnimationCts is { IsCancellationRequested: false })
if (_isDragging)
return;
var transition = GetTransition();
@ -976,6 +983,11 @@ namespace Avalonia.Controls
{
ResetViewportTransitionState();
ClearFractionalProgressContext();
// SyncScrollOffset is blocked during animation and the post-animation layout
// still sees a live CTS, so re-sync explicitly in case SelectedIndex changed.
if (ItemsControl is Carousel carousel)
SyncSelectionOffset(carousel.SelectedIndex);
});
}

Loading…
Cancel
Save