Для управления выбором элементов класс 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}" });
Аналогичный пример в 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 класса 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 предоставляет команду 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}"
Установка команды в коде 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 поддерживает программную установку выделенных элементов. Для этого его свойству 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 поддерживает двустороннюю привзку, то мы можем динамический менять значение этого свойства через привязанный объект. Например, определим в файле 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: