Reputation: 1236
So I am working with SQLite, CommunityToolkit.Mvvm.ComponentModel;
I have database containing a table of friends. I can bind this to a CollectionView. I am following https://www.youtube.com/watch?v=8_cqUvriwM8 but trying to use MVVM approach.
I can get it to work happily with SelectionChanged and an event, but not with SelectionChangedCommand and I can't get access to the Friend item in the list.
Here is the relevant xaml
<CollectionView Grid.Row="2"
x:Name="FriendsList"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding .}"
SelectionChanged="OnSelectionChanged" >
Here is the relevant part of the code (I'm using the code behind for the xaml just for testing)
public MainPage()
{
InitializeComponent();
this.BindingContext = this; //cool for binding the xaml to the code behind.
}
...
//This works fine (so why do I bother with mvvm?)
public void OnSelectionChanged(Object sender, SelectionChangedEventArgs e)
{
Console.WriteLine("Selection changed click");
Friend f = e.CurrentSelection[0] as Friend;
Console.WriteLine(f.LName);
}
//Can't get this to work, though it will register the click
public ICommand SelectionChangedCommand => new Command(SelectionChangedControl);
public void SelectionChangedControl()
{
Console.WriteLine("selection made");
}
My thinking was that if I could do this to get at the Friend item since the CommandParameter is, as I understand, to provide an object?
public ICommand SelectionChangedCommand => new Command<Friend>(SelectionChangedControl);
public void SelectionChangedControl(Friend f)
{
Console.WriteLine("selection made");
}
But the command doesn't even fire now. Clearly I am way off beam.
Any ideas please. (Oh by the way I have tried commenting out one or the other just in case).
BTW is there a reference (not MS docs) which explains this stuff in beginners terms? Is there an API reference to dot net Maui?
EDIT: From the documentation https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/selection
Single selection When the SelectionMode property is set to Single, a single item in the CollectionView can be selected. When an item is selected, the SelectedItem property will be set to the value of the selected item. When this property changes, the SelectionChangedCommand is executed (with the value of the SelectionChangedCommandParameter being passed to the ICommand, and the SelectionChanged event fires.
How do I get at value of the SelectionChangedCommandParameter, i.e. the row object, i.e. my Friend object?
EDIT2: Somehow I think I need to get at the CurrentSelection[0] but I don't know how.
I've learnt that I can do something like this (from the docs)
SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="Hello G"
and
public ICommand SelectionChangedCommand => new Command<string>( (String s) =>
{
Console.WriteLine($"selection made {s}");
});
and the command is picked up and displayed, so my thinking is that using {Binding .} is not what I want, but what do I bind to?
SelectionChangedCommandParameter ={Binding ???}
Thanks, G.
Upvotes: 4
Views: 13015
Reputation: 598
When you use .
at CollectionView.SelectionChangedCommandParameter
, it points at the BindingContext
of its parent view.
e.g. If your CollectionView
is in a ContentPage
, .
points at the BindingContext
of the ContentPage
.
If you want a reference of each item in FriendsList
, one of the solutions is to use SelectedItem
.
Try something like this:
<CollectionView
Grid.Row="2"
x:Name="FriendsList"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding Path=SelectedItem, Source={x:Reference FriendsList}}">
or
<CollectionView
Grid.Row="2"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding Path=SelectedItem, Source={RelativeSource Self}}">
References:
Bind to self (Source={RelativeSource Self}}
):
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/relative-bindings#bind-to-self
Upvotes: 7
Reputation: 179
I got hung up trying to bind multiple selections to the view model without linking it in the code behind. This page was the only relevant search result and helped a lot, but was missing a piece for multiple selections.
View.xaml
<CollectionView ItemsSource="{Binding DataItems}"
SelectedItems="{Binding SelectedData}"
SelectionMode="Multiple"
SelectionChangedCommand="{Binding SelectionChangedCommand}">
....
Couple of things to mention for the view model. I'm using CommunityToolkit.Mvvm, so the [ObservableProperty] annotation creates the property for you in proper camel case, and the [RelayCommand] for OnMethodName will drop the 'On' and just be MethodNameCommand.
ViewModel.cs
[ObservableProperty]
ObservableCollection<CustomDataItem> dataItems;
[ObservableProperty]
ObservableCollection<object> selectedData;
[RelayCommand]
void OnSelectionChanged()
{
foreach(var o in SelectedData)
{
if(o is CustomDataItem i)
...
}
}
The major takeaway though is that the SelectedItems must be a List<object> , they cannot be the <CustomDataItem>. I spent a couple hours searching and trying different things until I gave up and just linked the event handler in the code behind. But then I couldn't pre-select the items as described here until I changed them to the object list. So that list will populate both ways and you just have to cast it to the data type you're using.
Anyway, might've been obvious for some but maybe this will help anyone like me who just assumed the SelectedItems would be the same as the SelectedItem but in a list.
Upvotes: 7
Reputation: 1236
@Jason I'm laughing so much, I just figured it out and then came to post and saw your answer. Thankyou so much for your help.
For the record I found this post https://www.mfractor.com/blogs/news/migrating-listview-to-collectionview-in-xamarin-forms-interactivity
and eventually I figured out that I needed the SelectedItem as you pointed out. I think that because this wasn't needed (or is implicit) in the SelectionChanged click event.
Anyhow in my xaml
<CollectionView Grid.Row="2"
x:Name="FriendsList"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}"
SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding .}" >
In my code
public Friend SelectedItem { get; set; }
//respond to item select
public ICommand SelectionChangedCommand => new Command<Object>((Object e) =>
{
Console.WriteLine($"selection made {SelectedItem.FName}");
});
Your code is much simpler of course.
You pointed out that SelectionChangedCommandParameter="{Binding .}" was (probably) not needed, so what is it's purpose?
What is the object e that is being returned in my code? I assume it is related to the SelectionChangedCommandParameter?
In my immediate window I get
e
{Census.MainPage}
base: {Microsoft.Maui.Controls.ContentPage}
AddFriendCommand: {Microsoft.Maui.Controls.Command}
SelectedItem: {Census.Classes.Friend}
SelectionChangedCommand: {Microsoft.Maui.Controls.Command<object>}
And is it possible to trace through from the xaml to the code. For instance when I was trying to figure things out I would have liked to have trapped the item click event in the xaml and see what is was doing? (Especially since it didn't at times touch a breakpoint in my code.
Just idle questions and not expecting or needing an answer unless someone is so inclined.
Thank you much again @Jason, you are a star! :)
Upvotes: 0
Reputation: 89214
first, bind SelectedItem
SelectedItem="{Binding SelectedFriend}"
then in your VM create a property for that bound item
public Friend SelectedFriend { get; set; }
then in your Command you can use that property
public void SelectionChangedControl()
{
Console.WriteLine(SelectedFriend.Name);
}
Upvotes: 6