Search code examples
mauipickercollectionviewselectedindexchanged

Maui Picker in a CollectionView Item, Can't bind SelectedIndexChanged to the collection item object


I have cart items in a Grid inside a DataTemplate for showing CollectionView items. In the cart I have Add/Remove buttons with Commands that route to the CartViewModel and pass a CommandParameter of which CartViewItem they belong to.

Now I need to do the same with a picker for the pricing type drop down I can't figure out how to route the picker PropertyChanged to the model and pass along which item had its picker changed.

What's the best way to get the information I need on which cart item the PropertyChanged event is referring to? This code just sends the command to the CartPage Content Page with no info on which cart item it belongs to.

            <Picker Grid.Row="3" 
                    SelectedIndexChanged="Picker_SelectedIndexChanged" 
                    ItemsSource="{Binding CostTypeList}"
                    SelectedItem="{Binding CostType}"
                    />
            <Button Text="ADD" 
                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:CartViewModel}},Path=AddCommand}" 
                        CommandParameter="{Binding .}" 
                        Grid.Row="4" BackgroundColor="Blue"></Button>
            <Button Text="REMOVE" 
                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:CartViewModel}},Path=RemoveCommand}" 
                        CommandParameter="{Binding .}"  Grid.Row="4" Grid.Column="1" BackgroundColor="DarkRed"></Button>

And I can't get a syntax similar to the button syntax to work.

            <Picker Grid.Row="3" 
                    SelectedIndexChanged="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:CartViewModel}},Path=Picker_SelectedIndexChanged}"
                    ItemsSource="{Binding CostTypeList}"
                    SelectedItem="{Binding CostType}"
                    />

Gives this error before building

XFC0000 Cannot resolve type "clr-namespace:SVCA_Android.ViewModels:CartViewItem". sign-in-maui (net8.0-android34.0)

And this error after building

Error (active) XFC0009 No property, BindableProperty, or event found for "SelectedIndexChanged", or mismatching type between value and property. sign-in-maui (net8.0-android34.0)

public partial class CartViewModel : ObservableObject
{

    [ObservableProperty]
    ObservableCollection<CartViewItem> items;
    [ObservableProperty]
    public string costType;
    [ObservableProperty]
    public List<string> costTypeList;

    public partial class CartViewItem : ObservableObject
    {
        [ObservableProperty]
        public string costType;
        [ObservableProperty]
        public List<string> costTypeList;
    }

    public void Picker_SelectedIndexChanged(object sender, EventArgs e)
    {

    }
    [RelayCommand]
    async Task Add(CartViewItem i)
    {
        System.Diagnostics.Debug.WriteLine(i.ItemName + " " + i.ItemQty);
    }
    [RelayCommand]
    async Task Remove(CartViewItem i)
    {
        System.Diagnostics.Debug.WriteLine(i.ItemName + " " + i.ItemQty);
    }
}

Solution

  • SelectedIndexChanged is Picker's Event which can only be handled in code-behind file. If you want to handle this Event in the CartViewModel in MVVM pattern, consider using EventToCommandBehavior from CommunityToolkit.Maui Nuget.

    Here is my solution. To use communitytoolkit, please first install official CommunityToolkit.Maui Nuget. For more info, please refer to Getting Started with the .NET Multi-platform App UI (.NET MAUI) Community Toolkit.

    In XAML, when we change the selection of Picker, we would like to invoke SelectedIndexChangeCommand command in ViewModel. Here we use x:Reference Expression because RelativeBinding not work in EventToCommandBehavior. Then we pass the current cart item using {Binding .} through the CommandParameter

    <Picker Grid.Row="3" 
        SelectedIndexChanged="Picker_SelectedIndexChanged"     
        ItemsSource="{Binding CostTypeList}"
        SelectedItem="{Binding CostType}">
        <Picker.Behaviors>
            <toolkit:EventToCommandBehavior
                EventName="SelectedIndexChanged"
                Command="{Binding Source={x:Reference cartPage},Path=BindingContext.SelectedIndexChangeCommand}" 
                CommandParameter="{Binding .}"/>
        </Picker.Behaviors>
    </Picker>
    

    Please note that cartPage is the name of the ContentPage, so remember to set x:Name for the ContentPage,

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        .....
        
        xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
        x:Name="cartPage">
    

    In CartViewModel, let's define the SelectedIndexChangeCommand,

    [RelayCommand]
    async Task SelectedIndexChange(CartViewItem cartViewItem)
    {
        if(cartViewItem != null)
        {
            var item = cartViewItem as CartViewItem;
    
            Console.WriteLine(item.ItemName);
        }
       
    }
    

    Edited with a null check to prevent the exception on the initial loading.

    Now, we could get the current cart item through the parameter cartViewItem in CartViewModel.