.NET Maui SwipeItem command binding to viewmodel ancestor fails

Viewed 110

I have the following XAML

<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="model:LogEntry">
        <SwipeView>
            <SwipeView.RightItems>
                <SwipeItem Text="Delete"
                           BackgroundColor="Orange"
                           Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=RemoveLogEntryCommand}"
                           CommandParameter="{Binding .}" />
                <SwipeItem Text="Delete" 
                           BackgroundColor="Red"
                           IsDestructive="True" />
            </SwipeView.RightItems>
            <Grid Padding="10">
                <Frame HeightRequest="125"
                           Padding="0"
                           Style="{StaticResource CardView}">
                    <Frame.GestureRecognizers>
                        <TapGestureRecognizer CommandParameter="{Binding .}"
                                              Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=GotoLogEntryDetailsCommand}" />
                    </Frame.GestureRecognizers>
                    <Grid Padding="0"
                              ColumnDefinitions="80,*">

with the following ICommand declarations using the community toolkit

[RelayCommand]
private async Task GotoLogEntryDetails(LogEntry logEntry)
{
    if (logEntry == null)
        return;

    await _appNavigationService.GoTo($"{nameof(LogEntryDetailsPage)}", true,
        new Dictionary<string, object>
        {
            { "LogEntry", logEntry }
        });


}

[RelayCommand]
private async Task RemoveLogEntry(LogEntry logEntry)
{

}

If I put a breakpoint in RemoveLogEntry then click my delete button the breakpoint is never reached. If I put RemoveLogEntry on the tap gesture and tap am item then the breakpoint is reached, so I know that the code generator has created a valid ICommand.

Intellisense tells me that the argument on CommandParameter . is actually a LogEntry therefore I need to declare the viewModel type.

What is wrong with the SwipeItem's ancestor binding path?

1 Answers

UI elements (views) and viewmodels are in DIFFERENT hierarchies. A viewmodel is NEVER an ancestor of a view. So you can't find such an ancestor.

Instead, find the VIEW that is the ancestor. That view's BindingContext will be the corresponding viewmodel.

Change:

<SwipeItem ... Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:MainPageViewModel}}, Path=RemoveLogEntryCommand}"

To:

<SwipeItem ... Command="{Binding Source={RelativeSource AncestorType={x:Type MainPageView}}, Path=BindingContext.RemoveLogEntryCommand}"

However, I never use that syntax. I find it easier to give an x:Name to the element I want to refer to. Then can use a simple x:Reference Source:

<ContentPage
  ...
  x:Name="thisPage">
    ...
    <SwipeItem ... Command={Binding BindingContext.RemoveLogEntryCommand, Source={x:Reference thisPage}"
Related