Xamarin.forms: Button inside CollectionView does not invoke Command in ViewModel

Created on 28 Feb 2019  路  8Comments  路  Source: xamarin/Xamarin.Forms

Description

Button Command does not invoke inside CollectionView.

Steps to Reproduce

  1. Create a button inside CollectionView and set command Binding and click event on it.
  2. Create another button outside CollectionView and set command Binding and Click event on it.
  3. Run the app and click on both buttons.

Expected Behavior

Both Click Event and Command should be invoked.

Actual Behavior

But button inside the CollectionView only invode click event. Whereas the button outside CollectionView invokes both Command and Click event.

Basic Information

  • Version with issue:Xamarin.Forms (4.0.0.169046-pre5)
  • Last known good version: Not Applied
  • IDE: VSMac Community 8.0 Preview (8.0 build 1805)
  • Platform Target Frameworks:

    • iOS: Deployment Target: 8.0
    • Android: Target Android Version : Android 8.1 (API level 27)
  • Android Support Library Version:

  • Nuget Packages:
  • Affected Devices:Both iOS and Android

Screenshots

Reproduction Link

https://drive.google.com/file/d/1ft6tytOayoUdU8ehlsJm53pHi9Tl-JIG/view?usp=sharing

collectionview 3 bug

Most helpful comment

The BindingContext of a child of a CollectionView is set to the item for that child.

So just as {Binding Name} is looking for the name property on the Id class, {Binding ButtonCommand} is looking for the command on the Id class.

To make it work you need to something like the following:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyApp" x:Class="MyApp.MainPage" x:Name="Page">
    <ContentPage.BindingContext>
        <local:MainViewModel/>
        </ContentPage.BindingContext>
    <StackLayout Margin="10">
    <CollectionView ItemsSource="{Binding Names}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Spacing="0" VerticalOptions="Center" HorizontalOptions="Center">
                <Button HorizontalOptions="Center" BorderWidth="2" VerticalOptions="Center" FontSize="30" Text="{Binding Name}"
                        TextColor="Black" Clicked="Handle_Clicked" Command="{Binding Source={x:Reference Page} Path=BindingContext.ButtonCommand}"></Button>
                    </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    <Button HorizontalOptions="Center" BorderWidth="2" VerticalOptions="Center" FontSize="30" Text="ClickMe"
                        TextColor="Black" Clicked="Handle_Clicked" Command="{Binding ButtonCommand}"></Button>
        </StackLayout>
</ContentPage>

The page has a name added: Page and the button command binding is set explicitly to it using {x:Reference}

All 8 comments

The BindingContext of a child of a CollectionView is set to the item for that child.

So just as {Binding Name} is looking for the name property on the Id class, {Binding ButtonCommand} is looking for the command on the Id class.

To make it work you need to something like the following:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyApp" x:Class="MyApp.MainPage" x:Name="Page">
    <ContentPage.BindingContext>
        <local:MainViewModel/>
        </ContentPage.BindingContext>
    <StackLayout Margin="10">
    <CollectionView ItemsSource="{Binding Names}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Spacing="0" VerticalOptions="Center" HorizontalOptions="Center">
                <Button HorizontalOptions="Center" BorderWidth="2" VerticalOptions="Center" FontSize="30" Text="{Binding Name}"
                        TextColor="Black" Clicked="Handle_Clicked" Command="{Binding Source={x:Reference Page} Path=BindingContext.ButtonCommand}"></Button>
                    </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    <Button HorizontalOptions="Center" BorderWidth="2" VerticalOptions="Center" FontSize="30" Text="ClickMe"
                        TextColor="Black" Clicked="Handle_Clicked" Command="{Binding ButtonCommand}"></Button>
        </StackLayout>
</ContentPage>

The page has a name added: Page and the button command binding is set explicitly to it using {x:Reference}

@GalaxiaGuy Thanks for the solution. The Button Command is invoked after applying the changes. Still this is a bug. Button inside CollectionView should be able to invoke the Command in the ViewModel.

@HobDev

GalaxiaGuy is correct - the Command is not being bound to the Button in your ItemTemplate because the BindingContext of each item in the collection is an instance of the Id class; the Id class does not have a member called ButtonCommand.

You either need to reference the parent MainViewModel instance directly (as in GalaxiaGuy's solution) or you need to add a ButtonCommand member to the Id class.

@hartez The issue is not about BindingContext. It is related to Android Runtime. Please see #5554.

If it were an Android runtime issue, then your reproduction project would work on iOS. It does not.

Here's a working version, with the ButtonCommand moved to the Id class:
MyApp_Fixed.zip

It is working on Id class without any issue. GalaxiaGuy is correct. I will reference MainViewModel instance to ButtonCommand. Thank you.

@GalaxiaGuy still on this CollectionView, I used:
<TapGestureRecognizer Command="{Binding Source={x:Reference Name=DashPage}, Path=BindingContext.TopupCommand}" CommandParameter="{Binding .}"/>

this triggers the Command but the CommandParameter is not sending the current item even though I used {Binding .} It sends the first item instead

how can I fix this?

@nwikechisom Sounds like you might be hitting a different (more recent) bug - could you please open a new issue? Thanks.

Was this page helpful?
0 / 5 - 0 ratings