Выбор элементов в CollectionView

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

Для управления выбором элементов класс CollectionView определяет следующие свойства:

  • SelectionMode: режим выбора, представляет перечисление SelectionMode:

    • None: элементы нельзя выбрать. Значение по умолчанию

    • Single: можно выбрать один элемент

    • Multiple: можно выбрать несколько элементов

  • SelectedItem: выбранный элемент

  • SelectedItems: выбранные элементы в списке в виде объекта IList<object>

Рассмотрим получение выбранного элемента. Пусть у нас данные описываются следующим классом Person:

public class Person
{
    public string Name { get; set; } = "";
    public string Company { get; set; } = "";
}

В коде C# определим CollectionView и выведем текущий выделенный элемент в метку:

namespace HelloApp;

class StartPage : ContentPage
{
    public StartPage()
    {
        CollectionView collectionView = new CollectionView { VerticalOptions = LayoutOptions.Start};
        // устанавливаем режим выбора
        collectionView.SelectionMode = SelectionMode.Single;
        // определяем источник данных
        collectionView.ItemsSource = new List<Person>
        {
            new  Person { Name="Tom", Company ="Microsoft" },
            new  Person { Name="Sam", Company ="Google" },
            new  Person { Name="Bob", Company ="JetBrains" },
            new  Person { Name="Alice", Company ="Microsoft" },
            new  Person { Name="Kate", Company ="Google" },
            new  Person { Name="Amelia", Company ="JetBrains" }
        };
        // определяем шаблон данных
        collectionView.ItemTemplate = new DataTemplate(() =>
        {
            var nameLabel = new Label { FontSize = 20, TextColor = Color.FromArgb("#006064")};
            nameLabel.SetBinding(Label.TextProperty, "Name"); 

            var companyLabel = new Label();
            companyLabel.SetBinding(Label.TextProperty, "Company");

            return  new StackLayout
            {
                Children = { nameLabel, companyLabel },
                Margin = 8
            };
        });
        // метка для вывода выбранного элемента
        Label selectedLabel= new Label { Margin= 8, FontSize=18 };
        selectedLabel.SetBinding(Label.TextProperty, 
            new Binding { Source = collectionView, Path = "SelectedItem.Name", StringFormat="Selected: {0}" });
        Content = new StackLayout { selectedLabel,  collectionView} ;
    }
}

В данном случае установлен режим выбора одного элемента:

collectionView.SelectionMode = SelectionMode.Single;

К этому элементу привязана метка над CollectionView. Причем текст метки привязан именно к свойству Name выбранного элемента:

selectedLabel.SetBinding(Label.TextProperty, 
    new Binding { Source = collectionView, Path = "SelectedItem.Name", StringFormat="Selected: {0}" });
Получение выделенного элемента в CollectionView в .NET MAUI и C#

Аналогичный пример в 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">
    <StackLayout>
        <Label FontSize="18" Margin="5"
            Text="{Binding Source={x:Reference collectionView}, Path=SelectedItem.Name, StringFormat='Selected: {0}'}" />
        <CollectionView x:Name="collectionView" SelectionMode="Single">
        <CollectionView.ItemsSource>
            <x:Array Type="{x:Type local:Person}">
                <local:Person Name="Tom" Company="Microsoft"/>
                <local:Person Name="Sam" Company="Google"/>
                <local:Person Name="Bob" Company="JetBrains"/>
                <local:Person Name="Alice" Company="Microsoft"/>
                <local:Person Name="Kate" Company="Google"/>
                <local:Person Name="Amelia" Company="JetBrains" />
             </x:Array>
        </CollectionView.ItemsSource>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Margin="8">
                        <Label Text="{Binding Name}" FontSize = "20"  TextColor = "#006064"  />
                        <Label Text="{Binding Company}" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    </StackLayout>
</ContentPage>

Событие SelectionChanged

Событие SelectionChanged класса CollectionView позволяет отследить изменение выделенных элементов. В качестве параметра это событие принимает объект SelectionChangedEventArgs, который предоставляет два свойства

  • CurrentSelection: текущие выделенные элементы

  • PreviousSelection: ранее выбранные элементы

Поскольку CollectionView поддерживает множественный выбор, то оба свойства представляют список IReadOnlyList. Рассмотрим обработку этого события и для этого определим в 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">
    <StackLayout>
        <Label x:Name="selectedLabel" FontSize="18" Margin="5" />
        <CollectionView x:Name="collectionView" SelectionMode="Single"
                        SelectionChanged="OnCollectionViewSelectionChanged">
        <CollectionView.ItemsSource>
            <x:Array Type="{x:Type local:Person}">
                <local:Person Name="Tom" Company="Microsoft"/>
                <local:Person Name="Sam" Company="Google"/>
                <local:Person Name="Bob" Company="JetBrains"/>
                <local:Person Name="Alice" Company="Microsoft"/>
                <local:Person Name="Kate" Company="Google"/>
                <local:Person Name="Amelia" Company="JetBrains" />
             </x:Array>
        </CollectionView.ItemsSource>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Margin="8">
                        <Label Text="{Binding Name}" FontSize = "20"  TextColor = "#006064"  />
                        <Label Text="{Binding Company}" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    </StackLayout>
</ContentPage>

Здесь для события SelectionChanged установлен обработчик "OnCollectionViewSelectionChanged". Определим этот обработчик в файле MainPage.xaml.cs

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

    void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        string labelText = "";
        if (e.CurrentSelection.FirstOrDefault() is Person current)
            labelText = $"Current: {current.Name}";
        if (e.PreviousSelection.FirstOrDefault() is Person previous)
            labelText = $"{labelText}\nPrevious: {previous.Name}";
        selectedLabel.Text = labelText;
    }
}

В обработчике OnCollectionViewSelectionChanged получаем первый (и единственный) элемент из обоих коллекций и выводим информацию в метку:

Обработка выбора элемента в CollectionView в .NET MAUI и C#

Команда выбора элемента

Также CollectionView предоставляет команду SelectionChangedCommand, которая выполняется при изменении выбранного элемента. А через свойство SelectionChangedCommandParameter класса CollectionView в команду SelectionChangedCommand можно передать какой-нибудь параматр.

Например, в файле 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<Person?>(p =>
        {
            selectedLabel.Text = $"Selected: {p?.Name}";
        });
        BindingContext = this;
    }
}

В данном случае команда типизированная типом Person? и получает параметр этого типа - выбранный элемент и выводит его свойство Name в метку.

В файле 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">
    <StackLayout>
        <Label x:Name="selectedLabel" FontSize="18" Margin="5" />
        <CollectionView x:Name="collectionView" SelectionMode="Single" 
                        SelectionChangedCommand="{Binding SelectCommand}"
                        SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
        <CollectionView.ItemsSource>
            <x:Array Type="{x:Type local:Person}">
                <local:Person Name="Tom" Company="Microsoft"/>
                <local:Person Name="Sam" Company="Google"/>
                <local:Person Name="Bob" Company="JetBrains"/>
                <local:Person Name="Alice" Company="Microsoft"/>
                <local:Person Name="Kate" Company="Google"/>
                <local:Person Name="Amelia" Company="JetBrains" />
             </x:Array>
        </CollectionView.ItemsSource>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Margin="8">
                        <Label Text="{Binding Name}" FontSize = "20"  TextColor = "#006064"  />
                        <Label Text="{Binding Company}" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    </StackLayout>
</ContentPage>

Поскольку в коде странице в c# в качестве контекста привязки задана сама страница, то мы можем привязать команду SelectCommand

SelectionChangedCommand="{Binding SelectCommand}"

А параметр команды привязан к выбранному элементу в CollectionView:

SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}"
Команда выбора элемента в CollectionView в .NET MAUI и C#

Установка команды в коде C#:

collectionView.SelectionChangedCommand = new Command<Person?> (p =>
{
    selectedLabel.Text = $"Selected: {p?.Name}";
});
collectionView.SetBinding(CollectionView.SelectionChangedCommandParameterProperty, new Binding("SelectedItem", 
    source: RelativeBindingSource.Self));

Обработка множественного выбора

Для обработки множественного выбора мы можем отслеживать свойство SelectedItems, можем обрабатывать событие SelectionChanged, можем выполнять команду SelectionChangedCommand. Рассмотрим последний вариант. Определим в файле 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">
    <StackLayout>
        <Label x:Name="selectedLabel" FontSize="18" Margin="5" />
        <CollectionView x:Name="collectionView" SelectionMode="Multiple" 
                        SelectionChangedCommand="{Binding SelectCommand}"
                        SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItems}">
        <CollectionView.ItemsSource>
            <x:Array Type="{x:Type local:Person}">
                <local:Person Name="Tom" Company="Microsoft"/>
                <local:Person Name="Sam" Company="Google"/>
                <local:Person Name="Bob" Company="JetBrains"/>
                <local:Person Name="Alice" Company="Microsoft"/>
                <local:Person Name="Kate" Company="Google"/>
                <local:Person Name="Amelia" Company="JetBrains" />
             </x:Array>
        </CollectionView.ItemsSource>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Margin="8">
                        <Label Text="{Binding Name}" FontSize = "20"  TextColor = "#006064"  />
                        <Label Text="{Binding Company}" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    </StackLayout>
</ContentPage>

В данном случае для CollectionView установлен режим множественного выбора, а в команду SelectionChangedCommand передается список SelectedItems.

Определим в 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<IList<object>>(people =>
        { 
            string text = "Selected: ";
            
            foreach (var p in people)
            {
                if(p is Person person) text = $"{text} {person.Name},";
            }
            selectedLabel.Text = text;
        });
        BindingContext = this;
    }
}

Здесь команда SelectCommand получает список объектов, проходит по этому списку, преобразует каждый объект к типу Person и выводит значение свойства Name в метку.

Множественный выбор элементов в CollectionView в .NET MAUI и C#

Программное выделение элементов

Элемент CollectionView поддерживает программную установку выделенных элементов. Для этого его свойству SelectedItem (выбранный элемент) и SelectedItems (список выбранных элементов) можно присвоить соответствующие значение. Также эти свойства поддерживают двустороннюю привязку.

Например, пусть у нас данные представляет класс Person:

public class Person
{
    public string Name { get; set; } = "";
    public string Company { get; set; } = "";
}

Пусть CollectionView отображает список объектов Person. Программно выберем второй элемент списка:

class StartPage : ContentPage
{
    public StartPage()
    {
        var people = new List<Person>
        {
            new  Person { Name="Tom", Company ="Microsoft" },
            new  Person { Name="Sam", Company ="Google" },
            new  Person { Name="Bob", Company ="JetBrains" },
            new  Person { Name="Alice", Company ="Microsoft" },
            new  Person { Name="Kate", Company ="Google" },
            new  Person { Name="Amelia", Company ="JetBrains" }
        };
        CollectionView collectionView = new CollectionView { VerticalOptions = LayoutOptions.Start };
        // устанавливаем режим выбора
        collectionView.SelectionMode = SelectionMode.Single;
        // определяем источник данных
        collectionView.ItemsSource = people;
        // выбираем второй элемент
        collectionView.SelectedItem = people[1];
        // определяем шаблон данных
        collectionView.ItemTemplate = new DataTemplate(() =>
        {
            var nameLabel = new Label { FontSize = 20, TextColor = Color.FromArgb("#006064")};
            nameLabel.SetBinding(Label.TextProperty, "Name");
            var companyLabel = new Label();
            companyLabel.SetBinding(Label.TextProperty, "Company");
            return new StackLayout { Children = { nameLabel, companyLabel }, Padding=10 };
        });
        
        Content = collectionView;
    }
}

В качестве источника данных выступает список people. А в качестве выделенного элемента - второй элемент этого списка:

collectionView.SelectedItem = people[1];

Выделение элементов через двустороннюю привязку к SelectedItem

Поскольку свойство SelectedItem поддерживает двустороннюю привзку, то мы можем динамический менять значение этого свойства через привязанный объект. Например, определим в файле MainPage.xaml.cs следующую страницу:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace HelloApp;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new ViewModel();
    }
}
public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    Person? selectedPerson;
    public ObservableCollection<Person> People { get;}
    public ViewModel()
    {
        People = new ObservableCollection<Person>
        {
            new  Person { Name="Tom", Company ="Microsoft" },
            new  Person { Name="Sam", Company ="Google" },
            new  Person { Name="Bob",  Company ="JetBrains" },
            new  Person { Name="Alice", Company ="Microsoft" },
            new  Person { Name="Kate", Company ="Google" },
            new  Person { Name="Amelia", Company ="JetBrains" }
        };
        SelectedPerson = People[1];
    }
    public Person? SelectedPerson
    {
        get => selectedPerson;
        set
        {
            if (selectedPerson != value)
            {
                selectedPerson = value;
                OnPropertyChanged();
            }
        }
    }
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

Здесь для страницы в качестве контекста привязки устанавливаливается модель представления ViewModel, которая содержит источник данных - коллекцию People и выбранный элемент - свойство SelectedPerson. По умолчанию в качестве выбранного элемента применяется второй элемент коллекции People:

SelectedPerson = People[1];

В файле 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" >
    <StackLayout>
        <Label Text="{Binding SelectedPerson.Name, StringFormat='Selected: {0}'}" Margin="8" FontSize="20" />
        <CollectionView SelectionMode="Single" ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson}" >
            <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Padding="8">
                    <Label Text="{Binding Name}" FontSize = "20"  TextColor = "#006064"  />
                    <Label Text="{Binding Company}" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    </StackLayout>
</ContentPage>

Здесь к свойство SelectedItem элемента CollectionView привязано к свойству SelectedPerson модели представления. Соответственно после присвоения свойству SelectedPerson нового значения, также изменится и выделенный элемент в CollectionView.

Для большей показательности над CollectionView также определена метка, текст которой также привязан к свойству SelectedPerson:

Программное выделение элементов через привязку к SelectedItem в CollectionView в .NET MAUI и C#
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850