buttonxamarin.formsuicollectionviewexpander

Xamarin.Forms - ImageButtons in Expander do nothing as they're clicked


So I have a collectionview and inside of that I have an expander with 3 buttons. The problem is that once I touch one of these 3 buttons they don't do nothing even if they're binded to commands.

Here's how it looks like:

Here's the code:

<CollectionView HorizontalScrollBarVisibility="Never" 
                Margin="0,5,20,15"
                ItemsSource="{Binding Books}"
                VerticalOptions="StartAndExpand"
                ItemsLayout="HorizontalList"
                HeightRequest="210">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <StackLayout Margin="8,0">
                <xct:Expander>
                    <xct:Expander.Header>
                        <Grid RowDefinitions="*,auto">
                            <PanCake:PancakeView Grid.Row="0" Margin="0,0,20,0">
                                <ffimageloading:CachedImage Source="{Binding Cover}" HeightRequest="200" WidthRequest="145" Aspect="Fill"/>
                            </PanCake:PancakeView>
                            <Label Text="{Binding Title}" FontSize="14" Grid.Row="1" HorizontalOptions="Start" FontFamily="Inter" WidthRequest="145" LineBreakMode="TailTruncation" MaxLines="2" FontAttributes="Bold" TextColor="Black"/>
                        </Grid>
                    </xct:Expander.Header>
                    <xct:Expander.ContentTemplate>
                        <DataTemplate>
                            <StackLayout Orientation="Horizontal" Margin="0,10,0,0">
                                <ImageButton Source="trash_24.png" BackgroundColor="Transparent" HorizontalOptions="Start" Command="{Binding RemoveBookFromCollection}" CommandParameter="{Binding .}" Margin="0,0,0,0">
                                </ImageButton>
                                <ImageButton Source="cross_24.png" BackgroundColor="Transparent" Command="{Binding SetBookAvailability}" CommandParameter="{Binding .}" WidthRequest="24" HeightRequest="24" HorizontalOptions="Start"  Margin="10,0,0,0">
                                </ImageButton>
                                <ImageButton Source="check_24.png" BackgroundColor="Transparent" Command="{Binding SetBookAvailability}" CommandParameter="{Binding .}" HorizontalOptions="Start" Margin="10,0,0,0">
                                </ImageButton>
                            </StackLayout>
                        </DataTemplate>
                    </xct:Expander.ContentTemplate>
                </xct:Expander>                                   
            </StackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Solution

  • The DataTemplate has a different BindingContext than the rest of the Page. The DataTemplate inherits its BindingContext from each book instance in the Books collection. You need to use a relative binding in your command binding expression, e.g.:

    Command="{Binding RemoveBookFromCollection, Source={RelativeSource AncestorType={x:Type viewModel:YourViewModel}}"
    

    This will become apparent in the IDE, if you provide the x:DataType to the DataTemplate.

    You also don't need the ContentTemplate for the body of the Expander, since you're directly providing the content yourself.

    Since you didn't show the ViewModel, I will use a generic name, so you'll have to adapt it to your needs to make it work:

    <CollectionView
        ItemsSource="{Binding Books}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Book">
                <xct:Expander>
                    <xct:Expander.Header>
                        <Grid RowDefinitions="*,auto">
                            <PanCake:PancakeView Grid.Row="0" Margin="0,0,20,0">
                                <ffimageloading:CachedImage Source="{Binding Cover}" HeightRequest="200" WidthRequest="145" Aspect="Fill"/>
                            </PanCake:PancakeView>
                            <Label Text="{Binding Title}" FontSize="14" Grid.Row="1" HorizontalOptions="Start" FontFamily="Inter" WidthRequest="145" LineBreakMode="TailTruncation" MaxLines="2" FontAttributes="Bold" TextColor="Black"/>
                        </Grid>
                    </xct:Expander.Header>
                    <StackLayout Orientation="Horizontal" Margin="0,10,0,0">
                        <ImageButton
                            Source="trash_24.png"
                            Command="{Binding RemoveBookFromCollection, Source={RelativeSource AncestorType={x:Type viewModel:YourViewModel}}"
                            CommandParameter="{Binding .}">
                        </ImageButton>
    
                        <!-- etc ... -->
    
                    </StackLayout>
                </xct:Expander>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    

    The viewModel:YourViewModel should of course be replaced with your actual namespace and ViewModel. The namespace needs to be included in the XAML header of your page as an XML namespace (xmlns) and ideally also provide the x:DataType to the Page in order to use compiled bindings which also helps the IDE in pointing out binding issues to you and it will produce compiler errors for faulty binding expressions, e.g.:

    <ContentPage
        xmlns:viewModel="YourProject.SomeFolderWhereTheViewModelExists"
        x:DataType="viewModel:YourViewModel" />
    

    Using the compiled bindings and the x:DataType attribute is entirely optional, though, but will help with binding expressions. or similar.