Problem
I'm attempting a multi selection using CollectionView and MvvM. The (official docs don't do the greatest job of differentiating between normal code-behind and MVVM, and for us noobies that hurts.
I can get the single selection working, but making the leap to multiple selection is beyond me.
I will show my working code for single selection and discuss how to make it work for multiple selection. Maybe someone knows more than I?
Single Selection
Here's the working code for single selection:
Pass an ObservableCollection of type Person to a ModelView. Declare an instance of Person which will be the "selected object".
namespace Sandbox.ViewModel;
[QueryProperty("Persons", "Persons")]
public partial class SelectPageViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Person> persons;
[ObservableProperty]
private Person selectedPerson;
public SelectPageViewModel()
{
Persons = new();
}
}
In the View, create a CollectionView and make some good guesses for its attributes:
<Grid>
<Label Text="Select from List"/>
<CollectionView ItemsSource="{Binding Persons}"
SelectionMode="Single"
SelectedItem="{Binding SelectedPerson}"
SelectionChangedCommand="{Binding SelectionChangedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Person">
<Grid>
<Label Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
Back in the ViewModel, the SelectionChanged command: if the user is satisfied with their choice of SelectedPerson, I pass it back to the page from whence it came, otherwise I null the selection and return:
[RelayCommand]
private async Task SelectionChanged()
{
bool keepSelection = await App.Current.MainPage.DisplayAlert(SelectedPerson.Name, "Keep this selection?", "Yes", "No");
if (keepSelection)
{
Dictionary<string, object> throwParam = new()
{
{ "SelectedPerson", SelectedPerson }
};
await Shell.Current.GoToAsync("..", throwParam);
}
// else clear the selection and return
SelectedPerson = null;
return;
}
Multiple Selection
After much wrestling, here is working code. Something very important: note the type of the ObservableCollection that is used to in the binding to the collection (hint, it's Object).
Another Edit (my current code)
My current code is the same as the above code, but I will show both ViewModel and View in total, plus screenshots of the List that's supposed to be populated.
ViewModel:
namespace Sandbox.ViewModel;
[QueryProperty("Persons","Persons")]
public partial class SelectPageViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Person> persons;
[ObservableProperty]
private ObservableCollection<Object> selectedPersons;
[ObservableProperty]
private Person selectedPerson;
public SelectPageViewModel()
{
Persons = new();
SelectedPersons = new();
}
[RelayCommand]
private void SelectionChanged()
{
// every time something is selected, the object is added to SelectedPersons automagically.
int a = SelectedPersons.Count; // will +1 every time
}
}
View:
<?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"
xmlns:viewmodel="clr-namespace:Sandbox.ViewModel"
xmlns:model="clr-namespace:Sandbox.Model"
x:DataType="viewmodel:SelectPageViewModel"
x:Class="Sandbox.View.SelectPage"
Title="SelectPage">
<Grid RowDefinitions="Auto,Auto" Padding="10">
<Label Grid.Row="0"
Text="Select from List"
FontSize="Large"
FontAttributes="Bold" />
<CollectionView Grid.Row="1"
ItemsSource="{Binding Persons}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedPersons, Mode=TwoWay}"
SelectionChangedCommand="{Binding SelectionChangedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Person">
<Grid Padding="10">
<Label Text="{Binding Name}"
FontSize="Medium" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
create a property in your VM (note that it needs to be a collection of object (see this question)
initialize them
then bind your
CollectionViewto itif the user selects 3 rows, those 3 objects will be contained in
SelectedPersons.SelectedPersonswill be a subset of yourItemsSourcePersons