Взаимодействие с CarouselView. Обработка выбора элемента

Последнее обновление: 25.01.2023

Рассмотрим, как взаимодействовать с элементами в CarouselView. Пусть данные представленны следующим классом Product, который описывает товар:

class Product
{
    public string Name { get; set; } = "";
    public string Description { get; set; } = "";
    public string ImagePath { get; set; } = "";
}

свойство Name указывает на название товара, свойство Description - на некоторое описание, а ImagePath - на путь к файлу изображений.

Для хранения файлов изображений поместим в в проект в папку Resources/Images несколько изображений-логотипов для разных товаров:

Изображения для CarouselView в .NET MAUI и C#

Текущий элемент

Свойство CurrentItem позволяет получить текущий отображаемый элемент. Это свойство поддерживает двустороннюю привязку и имеет значение null, если никакие данные не отображаются.

Например, определим в коде MainPage.xaml следующую страницу:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage"
             xmlns:local="clr-namespace:HelloApp">
    <VerticalStackLayout Padding="5">
        <Label Text="{Binding Source={x:Reference carouselView}, Path=CurrentItem.Name}" />
        <CarouselView x:Name="carouselView">
            <CarouselView.ItemsSource>
                <x:Array Type="{x:Type local:Product}">
                    <local:Product Name="Huawei P50" ImagePath="huaweip50_sm.jpg" Description="Цена 59000" />
                    <local:Product Name="iPhone 14" ImagePath="iphone14_sm.jpg" Description="Цена 65000" />
                    <local:Product Name="Realme GT2 Pro" ImagePath="realmegt2pro_sm.jpg" Description="Цена 41000" />
                    <local:Product Name="Xiaomi 12X" ImagePath="xiaomi12x_sm.jpg" Description="Цена 53999" />
                </x:Array>
            </CarouselView.ItemsSource>
            <CarouselView.ItemTemplate>
                <DataTemplate>
                    <Frame>
                        <VerticalStackLayout>
                            <Label Text="{Binding Name}" FontAttributes="Bold" FontSize="18" HorizontalTextAlignment="Center"/>
                            <Image WidthRequest="150" HeightRequest="150" Source="{Binding ImagePath}"   />
                            <Label Text="{Binding Description}" HorizontalTextAlignment="Center" />
                        </VerticalStackLayout>
                    </Frame>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </VerticalStackLayout>
</ContentPage>

Здесь метка верхняя Label привязана к текущему выбранному элементу:

<Label Text="{Binding Source={x:Reference carouselView}, Path=CurrentItem.Name}" />

Стоит отметить, что привязка идет к свойству "Name". Хотя по умолчанию свойство CurrentItem представляет тип object, но .NET MAUI может преобразовать объект в его непосредственный тип - в данном случае тип Product и соответственно получить значение свойства Name.

И при перемещении это значение будет меняться

CurrentItem и получение текущего элемента и CarouselView в .NET MAUI и C#

Обработка изменения текущего элемента

С помощью события CurrentItemChanged можно отследить выбор нового элемента. В качестве параметра это событие принимает объект типа CurrentItemChangedEventArgs, который имеет два свойства:

  • PreviousItem: элемент, который ранее был выбран

  • CurrentItem: текущий выбранный элемент

Например, обработаем выбор элемента. Для этого определим в файле MainPage.xaml следующий интерфейс:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage"
             xmlns:local="clr-namespace:HelloApp">
    <VerticalStackLayout Padding="5">
        <Label x:Name="header" FontSize="18"/>
        <CarouselView x:Name="carouselView" CurrentItemChanged="carouselView_CurrentItemChanged">
            <CarouselView.ItemsSource>
                <x:Array Type="{x:Type local:Product}">
                    <local:Product Name="Huawei P50" ImagePath="huaweip50_sm.jpg" Description="Цена 59000" />
                    <local:Product Name="iPhone 14" ImagePath="iphone14_sm.jpg" Description="Цена 65000" />
                    <local:Product Name="Realme GT2 Pro" ImagePath="realmegt2pro_sm.jpg" Description="Цена 41000" />
                    <local:Product Name="Xiaomi 12X" ImagePath="xiaomi12x_sm.jpg" Description="Цена 53999" />
                </x:Array>
            </CarouselView.ItemsSource>
            <CarouselView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <Frame BackgroundColor="White">
                            <VerticalStackLayout>
                                <Label Text="{Binding Name}" FontAttributes="Bold" FontSize="18" HorizontalTextAlignment="Center"/>
                                <Image WidthRequest="150" HeightRequest="150" Source="{Binding ImagePath}"   />
                                <Label Text="{Binding Description}" HorizontalTextAlignment="Center" />
                            </VerticalStackLayout>
                        </Frame>
                    </StackLayout>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </VerticalStackLayout>
</ContentPage>

Здесь для события CurrentItemChanged устанавливается обработчик carouselView_CurrentItemChanged. И в файле MainPage.xaml.cs определим данный обработчик :

namespace HelloApp;

public partial class MainPage : ContentPage
{
    public MainPage()
	{
		InitializeComponent();
    }

    private void carouselView_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
    {
        Product? current = e.CurrentItem as Product;
        Product? previous = e.PreviousItem as Product;
        header.Text=$"Current: {current?.Name}  Previous: {previous?.Name}";
    }
}

В обработчике получаем текущий и ранее нажатый элементы и выводим их в метке Label.

Обработка события изменения текущего элемента и CarouselView в .NET MAUI и C#

Обработка команды CurrentItemChangedCommand

Для получения нового выбранного элемента у CarouselView также можно использовать свойство CurrentItemChangedCommand, которое представляет команду, вызываемую при изменении текущего элемента. Кроме того, с помощью свойства CurrentItemChangedCommandParameter можно передать в команду некоторое значение.

Так, определим в файле MainPage.xaml.cs команду для обработки выбора элемента:

using System.Windows.Input;

namespace HelloApp;

public partial class MainPage : ContentPage
{
    public ICommand SelectCommand { get; set; }
    public MainPage()
	{
		InitializeComponent();
        SelectCommand = new Command<Product?>(p =>
        {
            header.Text=$"You have selected: {p?.Name}";
        });
        BindingContext = this;
    }
}

В данном случае команда получает выбранный объект Product и отображает его название во всплывающем сообщении. Благодаря тому, что в качестве контекста привязки страницы выступает сама эта страница, то в коде xaml мы сможем выполнить привязку к команде.

А в файле MainPage.xaml определим следующий интерфейс:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage"
             xmlns:local="clr-namespace:HelloApp">
    <VerticalStackLayout Padding="5">
        <Label x:Name="header" FontSize="18"/>
        <CarouselView CurrentItemChangedCommand="{Binding SelectCommand}"
                      CurrentItemChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=CurrentItem}">
            <CarouselView.ItemsSource>
                <x:Array Type="{x:Type local:Product}">
                    <local:Product Name="Huawei P50" ImagePath="huaweip50_sm.jpg" Description="Цена 59000" />
                    <local:Product Name="iPhone 14" ImagePath="iphone14_sm.jpg" Description="Цена 65000" />
                    <local:Product Name="Realme GT2 Pro" ImagePath="realmegt2pro_sm.jpg" Description="Цена 41000" />
                    <local:Product Name="Xiaomi 12X" ImagePath="xiaomi12x_sm.jpg" Description="Цена 53999" />
                </x:Array>
            </CarouselView.ItemsSource>
            <CarouselView.ItemTemplate>
                <DataTemplate>
                    <Frame>
                        <VerticalStackLayout>
                            <Label Text="{Binding Name}" FontAttributes="Bold" FontSize="18" HorizontalTextAlignment="Center"/>
                            <Image WidthRequest="150" HeightRequest="150" Source="{Binding ImagePath}"   />
                            <Label Text="{Binding Description}" HorizontalTextAlignment="Center" />
                        </VerticalStackLayout>
                    </Frame>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </VerticalStackLayout>
</ContentPage>

Здесь прежде всего устанавливается привязка к команде SelectCommand:

CurrentItemChangedCommand="{Binding SelectCommand}"

А в качестве параметра в команду передается текущий выбранный элемент из свойства CurrentItem:

CurrentItemChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=CurrentItem}"
Команда для изменения текущего элемента и CarouselView в .NET MAUI и C#

Аналогичный пример в коде C#:

namespace HelloApp;
class StartPage : ContentPage
{
    public StartPage()
    {
        Label currentLbl = new Label(); // для вывода текущего элемента
        CarouselView carouselView = new CarouselView
        {
            VerticalOptions = LayoutOptions.Start
        };
        
        carouselView.ItemsSource = new List<Product>
        {
            new Product {Name="Huawei P50", ImagePath="huaweip50_sm.jpg", Description = "Цена 59000" },
            new Product {Name="iPhone 14", ImagePath="iphone14_sm.jpg", Description = "Цена 65000" },
            new Product {Name="Realme GT2 Pro", ImagePath="realmegt2pro_sm.jpg", Description = "Цена 41000"},
            new Product {Name="Xiaomi 12X", ImagePath="xiaomi12x_sm.jpg", Description = "Цена 53999" },

        };
        // определяем шаблон данных
        carouselView.ItemTemplate = new DataTemplate(() =>
        {
            Label header = new Label
            {
                FontAttributes = FontAttributes.Bold,
                HorizontalTextAlignment = TextAlignment.Center,
                FontSize = 18
            };
            header.SetBinding(Label.TextProperty, "Name");

            Image image = new Image { WidthRequest= 150, HeightRequest = 150 };
            image.SetBinding(Image.SourceProperty, "ImagePath");

            Label description = new Label { HorizontalTextAlignment = TextAlignment.Center };
            description.SetBinding(Label.TextProperty, "Description");

            StackLayout stackLayout = new StackLayout() { header, image, description};
            Frame frame = new Frame();
            frame.Content = stackLayout;
            return frame;
        });

        // определяем команду и параметр
        carouselView.CurrentItemChangedCommand = new Command<Product?>(p =>
        {
            currentLbl.Text= $"You have selected: {p?.Name}";
        });
        carouselView.SetBinding(CarouselView.CurrentItemChangedCommandParameterProperty, new Binding("CurrentItem", source: RelativeBindingSource.Self));
        Content =  new VerticalStackLayout { currentLbl, carouselView };
    }
}

Кастомная обработка выбора элемента

Стоит отметить, что между операционными системами могут наблюдаться различия в поведении. Например, на Windows на момент написания текущей статьи свойство CurrentItem никак не изменяется, если мы перемещаемся по списку с помощью клавиатуры или наживаем на элементы. Кроме того, может возникнуть необходимость определить свой механизм выбора элемента. Для этого мы можем обращаться непосредственно к элементам внутри CarouselView. Например, обработаем нажатие на элемент. Для этого определим в файле MainPage.xaml.cs следующий код:

using System.Windows.Input;

namespace HelloApp;

public partial class MainPage : ContentPage
{
    public ICommand SelectCommand { get; set; }
    public MainPage()
    {
        InitializeComponent();
        SelectCommand = new Command<Product>(async p =>
        {
            await DisplayAlert("Notification", $"You have selected: {p.Name}", "ОK");
        });
        BindingContext = this;
    }
}

Здесь в классе страницы определена команда, которая получает нажатый элемент и выводит его название в сообщении.

Теперь определим код MainPage.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage"
             xmlns:local="clr-namespace:HelloApp">
    <CarouselView x:Name="carouselView"  VerticalOptions="Start">
        <CarouselView.ItemsSource>
            <x:Array Type="{x:Type local:Product}">
                <local:Product Name="Huawei P50" ImagePath="huaweip50_sm.jpg" Description="Цена 59000" />
                <local:Product Name="iPhone 14" ImagePath="iphone14_sm.jpg" Description="Цена 65000" />
                <local:Product Name="Realme GT2 Pro" ImagePath="realmegt2pro_sm.jpg" Description="Цена 41000" />
                <local:Product Name="Xiaomi 12X" ImagePath="xiaomi12x_sm.jpg" Description="Цена 53999" />
            </x:Array>
        </CarouselView.ItemsSource>
        <CarouselView.ItemTemplate>
            <DataTemplate>
                <Frame>
                    <VerticalStackLayout>
                        <Label Text="{Binding Name}" FontAttributes="Bold" FontSize="18" HorizontalTextAlignment="Center"/>
                        <Image WidthRequest="150" HeightRequest="150" Source="{Binding ImagePath}"   />
                        <Label Text="{Binding Description}" HorizontalTextAlignment="Center" />
                    </VerticalStackLayout>
                    <Frame.GestureRecognizers>
                        <TapGestureRecognizer 
                                Command="{Binding Source={x:Reference carouselView}, Path=BindingContext.SelectCommand}"
                                CommandParameter="{Binding}"/>
                    </Frame.GestureRecognizers>
                </Frame>
            </DataTemplate>
        </CarouselView.ItemTemplate>
    </CarouselView>
</ContentPage>

Здесь каждый отдельный элемент фактически представлен визуальным элементом Frame. и как и в общем случае мы можем установить для этого элемента (как и для других элементов) объект TapGestureRecognizer для обработки нажатия по этого элементу:

<Frame.GestureRecognizers>
    <TapGestureRecognizer 
        Command="{Binding Source={x:Reference carouselView}, Path=BindingContext.SelectCommand}"
        CommandParameter="{Binding}"/>
</Frame.GestureRecognizers>

Здесь привязка к команде SelectCommand. Поскольку она определена в контексте элемента CarouselView, то для привязки к ней идет обращение к контексту привязки CarouselView Path=BindingContext.SelectCommand

В качестве параметра в команду будет передаваться текущий элемент (CommandParameter="{Binding}")

Таким образом, по нажатию на элемент будет срабатывать команда SelectCommand:

Обработка нажатия на элемент в CarouselView в приложении на .NET MAUI и C#
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850