0
votes

I am working on a Xamarin.Forms page displaying a list of items using MVVM pattern.

I can not get the Command property of the TapGestureRecognizer object of the list items working. It does not even fire, no matter what I try. This command property should reference a single ICommand property on the VM for all the listview elements. The CommandParameter property should select between the elements based on their Id.

The Tapped event seems to be working. I am not sure whether my databinding of the Command property to the VM is faulty or Xamarin.Forms has a bug here.

I have tried different settings for the data binding of TapGestureRecognizer's Command property to my ViewModel and I have also played with the InputTransparent properties on the nested View-elements. No change here: Command does not fire

View XAML header:

<ContentPage ...
             x:Class="Ch9.MainPage2"
             x:Name="page"
             >

View has a ViewModel property which is the viewmodel:

public partial class MainPage2 : ContentPage
{
    public MainPage2ViewModel ViewModel
    {
       get => BindingContext as MainPage2ViewModel;
       set => BindingContext = value;
    }

        public MainPage2()
        {
            InitializeComponent();
            ViewModel = new MainPage2ViewModel(
            ...injecting components...
             );
        }
}

the viewmodel has an ItemTappedCommand property:

public ICommand ItemTappedCommand { get; private set; }
...
ItemTappedCommand = new Command<int>(async id => await OnItemTappedCommand(id));

Inside the page I have a stacklayout as content-root and inside the stacklayout there is a listview. To keep the code small, I have omitted what I could, what I think is not relevant

<ListView
    ItemsSource="{Binding SearchResults}">
    <ListView.ItemTemplate>
    <DataTemplate>                        
        <ViewCell>
            <StackLayout>
                <StackLayout.GestureRecognizers>
                      <TapGestureRecognizer 
                            BindingContext="{x:Reference page}"                                        
                            Command="{Binding Path=ViewModel.ItemTappedCommand}" CommandParameter="{Binding Id}"  NumberOfTapsRequired="1"/>
                </StackLayout.GestureRecognizers>

                                <Image InputTransparent="True">
                                    <Image.Source>
                                        <UriImageSource Uri="{Binding ImgSmSrc}"
                                                    CacheValidity="0"
                                                    CachingEnabled="False"                                                    
                                                    />
                                    </Image.Source>
                                </Image>
                               <StackLayout InputTransparent="True">
                              some content...
                              </StackLayout>

                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
1
Use the ItemSelected or ItemTapped event instead of GestureRecognizer.Sparsha Bhattarai
I would like to use MVVM pattern without code behind if and wherever possible. Currently I am refactoring from code behind (where I used ItemTapped event) to MVVM where I would like to use CommandsJ4ni
Where do you actually set the ViewModel as the BindingContext of your View? Your have a getter setter in your code behind but I don't see the assignment of the ViewModelMouse On Mars
Hi Mouse! I set it in the page's constructor. I have updated the code listing accordingly. i have omitted before because I didnt want to pollute the code with noise.J4ni
Does any other binding work on this page?Mouse On Mars

1 Answers

2
votes

You've changed your TapGestureRecognizer's BindingContext to your current content page but it could not notice the customized defined property ViewModel, you can only consume the ContentPage's property.

So {Binding Path=ViewModel.ItemTappedCommand} this binding will fail.

I recommend you to adjust your binding like:

<ListView x:Name="listView" ItemsSource="{Binding SearchResults}" HasUnevenRows="True">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout>
                    <StackLayout.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding Path= BindingContext.ItemTappedCommand, Source={x:Reference listView}}" 
                                            CommandParameter="{Binding Id}"  
                                            NumberOfTapsRequired="1"/>
                    </StackLayout.GestureRecognizers>
                    <!--onther stuff-->
                    <!--...-->

                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

The listView's binding context is your view model which has been set using code behind. At last you could consume the ItemTappedCommand there.

Moreover, as you want to access the Id, I only changed the Command's Source instead of the whole gesture's binding context. Or you won't retrieve that because it belongs to the element of SearchResults not view model itself.