1

My problem is similar as this one MenuItem + CommandParameter = null ! Why?

The CommandParameter="{Binding .}" in my MenuItem, nested in ViewCell.ContextActions is always null when I read it. I also tried CommandParameter="{Binding}".

Here is the full code:

View

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:viewmodel="clr-namespace:CookUs.ViewModel"
         xmlns:model="clr-namespace:CookUs.Model"
         x:Class="CookUs.View.ViewRecipePage"
         x:DataType="viewmodel:ViewRecipeViewModel"
         Title="{Binding Recipe.Name}">

<ScrollView>
    <VerticalStackLayout>
        <ListView ItemsSource="{Binding Recipe.Ingredients}" 
                  x:Name="listView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <ViewCell.ContextActions>
                            <MenuItem Text="Add to Cart" IconImageSource="add_to_cart.png"
                                      Command="{Binding Source={x:Reference listView}, Path=BindingContext.AddToCartCommand}"
                                      CommandParameter="{Binding .}" />
                        </ViewCell.ContextActions>
                        <StackLayout x:DataType="model:Ingredient">
                            <Label Text="{Binding Name}" FontAttributes="Bold" FontSize="Small" HorizontalTextAlignment="Center"/>
                            <Label Text="{Binding Quantity}" FontSize="Micro" HorizontalTextAlignment="Center"/>
                        </StackLayout>

                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </VerticalStackLayout>
</ScrollView>

ViewModel

public Command AddToCartCommand { get; }

public ViewRecipeViewModel()
{
    AddToCartCommand = new Command(OnAddToCartAsync);
}

private async void OnAddToCartAsync(object obj)
{
        
if (obj == null) return;
Ingredient i = obj as Ingredient;
if(!(await DataStore.AddToCartAsync(i))) {
    await Application.Current.MainPage.DisplayAlert("Error", "Failed to add to cart", "OK");
}
OnPropertyChanged(nameof(Cart));  
}

Model

namespace CookUs.Model
{
    public class Ingredient
    {
        public string Name { get; set; }
        public string Quantity { get; set; }
    }
}
Abristonks
  • 83
  • 6
  • please post the relevant portions of your model – Jason Dec 16 '22 at 16:06
  • 1
    The problem in your Binding is that you need to pass `.` as an argument, it should be: `CommandParameter="{Binding .}"`, that's how you can pass the entire item as a parameter. – Julian Dec 16 '22 at 18:52
  • You should still post your command definition, because it will make it easier to understand how your `OnAddToCartAsync()` method gets called – Julian Dec 16 '22 at 19:02
  • If my answer doesn't solve your problem, can you show the rest of your XAML or at least the relevant bits including the root node? Are you by any chance setting `x:DataType`? – Julian Dec 16 '22 at 19:26

1 Answers1

3

Update

Your problem is the x:DataType. When using Compiled Bindings, you need to set the correct DataType everywhere where the BindingContext changes. In this case, you'll need to add the x:DataType="Ingredient" on the DataTemplate instead of the StackLayout:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:viewmodel="clr-namespace:CookUs.ViewModel"
         xmlns:model="clr-namespace:CookUs.Model"
         x:Class="CookUs.View.ViewRecipePage"
         x:DataType="viewmodel:ViewRecipeViewModel"
         Title="{Binding Recipe.Name}">

<ScrollView>
    <VerticalStackLayout>
        <ListView ItemsSource="{Binding Recipe.Ingredients}" 
                  x:Name="listView">
            <ListView.ItemTemplate>
                <DataTemplate
                    x:DataType="model:Ingredient">
                    <ViewCell>
                        <ViewCell.ContextActions>
                            <MenuItem Text="Add to Cart" IconImageSource="add_to_cart.png"
                                      Command="{Binding Source={x:Reference listView}, Path=BindingContext.AddToCartCommand}"
                                      CommandParameter="{Binding .}" />
                        </ViewCell.ContextActions>
                        <StackLayout>
                            <Label Text="{Binding Name}" FontAttributes="Bold" FontSize="Small" HorizontalTextAlignment="Center"/>
                            <Label Text="{Binding Quantity}" FontSize="Micro" HorizontalTextAlignment="Center"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </VerticalStackLayout>
</ScrollView>

From previous version of the answer:

Notice the . that is part of the binding:

CommandParameter="{Binding .}"

It means to pass the entire item as a parameter.

Julian
  • 5,290
  • 1
  • 17
  • 40
  • I tried, and it is still null. On the Microsoft documentation it was `{Binding}`. I updated my post to contain all the informations – Abristonks Dec 17 '22 at 08:47
  • I've updated my answer. You have the `x:DataType="model:Ingredient"` in the wrong level. – Julian Dec 17 '22 at 09:19